Arduino Programming Advanced Methods
Arduino Programming Advanced Methods
Arduino Hardware
The piece of hardware that the user will end up interacting with is the
‘Arduino board .’ The software that we created using the Arduino IDE
directly interacts with this board, executing any instructions embedded
within the program. Generally, people do not use just the Arduino board
itself; on the contrary, there are extra components available that pop onto
the Arduino board extending its usability. For instance, we can attach things
like switches, thermal-sensors, gyroscopes, pressure sensors, LEDs, output
displays, and even motors to the Arduino board, all depending on what the
project requires.
When using the Arduino environment for a project, users can choose from a
variety of Arduino hardware boards according to the needs of the project
and the Arduino software. There are a variety of Arduino boards available
in the community for users to pick up. There are official Arduino boards
released by the Arduino team, as well as Arduino boards that are released
by the members of the community. Suppose you are picking up a project
from the community and modifying it according to your needs. In that case,
it's recommended to use the tools as specified by the original project
designer or swap a few things in and out as you see fit. Arduino boards
come in a variety of form-factors. You can find boards that are as small as
credit cards and boards that are just large enough to design a project that
features applications such as wearables. However, one thing to note is that
different sized boards do not have the same specifications. Generally,
smaller Arduino boards have fewer connection points for external
peripherals and an underwhelming processor. In contrast, bigger boards
feature a more robust processor and an increased number of connections
(since the size is bigger, it can easily accommodate more ports). A quick
run-through of the popular Arduino boards has been given below;
Arduino Nano
Arduino Bare Bones
Boarduino
Seeduino
Arduino Teensy and Arduino Teensy plus
}
void loop()
{
}
If we are using this code as a sketch program, we will have to compile it. So
let’s go over the process of exporting code on to Arduino boards. First, we
will write this code (or copy it if you want to) in the IDE, and then we will
compile it using the ‘Compile ’ option in the IDE interface and export it to
the board using the ‘Upload ’ option either through the ‘Files ’ menu or
through the interface button. Once the file is uploaded, we then save it on
our system as we might need it in the future. To do this, we use the ‘Save ’
or ‘Save As ’ option in the ‘Files ’ menu.
When you save a sketch program, by default, the IDE software will set the
location to a directory named ‘Arduino ’ in the user’s ‘My Documents .’
This happens when we are using a Windows system; if it’s a Mac, then the
default file directory will be inside the ‘Documents ’ folder. It is
recommended to name your sketch programs such that they reflect their
usage or the instructions; however, the name of the sketch cannot be the
same as the example sketch files. This makes it easier when you are dealing
with a lot of sketch programs and saves you from the trouble of having to
look through each program and see what it does before you find the sketch
you’re looking for. Using the default save location of the IDE has its perks
as well. Sketch program files stored in this directory are automatically
displayed within the ‘Sketchbook menu ’ of the IDE, making it quicker
and easier for the user to access.
One of the weaknesses of the Arduino IDE is its absence of sketch version
management. This means that the IDE application does not have the
functionality to revert the modifications made to a sketch over different
iterations. So, if you find yourself frequently modifying your sketch files,
then it is recommended that you keep each modified version of the program
saved on your system. In this way, if you wish to use an older version of the
same sketch program, you can do so.
If you are creating an Arduino IDE program from scratch or extending the
example sketches, then it’s a good idea to compile the program frequently.
In this way, you can easily fix any errors that pop up as you finalize the
code without having to deal with a whole bunch of errors at the end and
getting overwhelmed with the necessary changes.
Before we move on, there’s one final thing that needs to be discussed and it
is the folder that contains the sketch file itself. In the Arduino IDE, the
sketch programs must be stored within a folder that has a name matching
the program's name.
delay(rate);
}
Before we can even leverage the sensor's functionality, it needs to be
connected to the board so the two components can communicate with each
other. To do this, we will have to create a simple circuit that will consist of
the LDR sensor itself and a suitable resistor. A resistor with a rating
between 1000 to 10,000 ohm will be work just fine. Now that we know how
the components need to be connected, let’s discuss the program
demonstrated above. The core concept around which this program is
designed is to define a certain value which corresponds to the intensity of
the LED light. The brightness of the light emitted by the LED at pin ‘0’ will
be interpreted by the LDR sensor connected to the board and its value will
change accordingly. Based on this changing value, the sensor will increase
or decrease the corresponding LED's voltage level accordingly. To read the
value being pushed out by the sensor, we use a function known as
‘analogRead() .’ So, if the sensor detects that the light’s brightness is high,
then the value will also be read as high by the function (in this case, the
value will be ‘800’). Similarly, if the light's brightness is low, then the value
given will be low as well (in this case, ‘200’). The voltage of the LED will
be controlled by the LDR sensor accordingly.
We can specify the flickering rate of the LED by using the ‘map()’ function
as well. The following code shown below demonstrates this;
const int ledPin = 13; // LED connected to digital pin 13
}
void loop()
{
We can also use the Serial Monitor available in Arduino to take a look at
various stuff to fine-tune the sketch program. For instance, we can observe
the frequency at which the LED blinks at certain variable values. So, we
can precisely know the blinking rate at a specific value and then set the
value at which we want it to blink. This frequency is displayed in the IDE’s
Serial Monitor. The following lines of code demonstrate the usage of Serial
Monitors;
const int ledPin = 13; // LED connected to digital pin 13
const int sensorPin = 0; // connect sensor to analog input 0
// the next two lines set the min and max delay between blinks
const int minDuration = 100; // minimum wait between blinks
const int maxDuration = 1000; // maximum wait between blinks
void setup()
{
pinMode(ledPin, OUTPUT); // enable output on the led pin
Serial.begin(9600); // initialize Serial
}
void loop()
{
int rate = analogRead(sensorPin); // read the analog input
// the next line scales the blink rate between the min and max values
rate = map(rate, 200,800,minDuration, maxDuration); // convert to blink
rate
Serial.println(rate); // print rate to serial monitor
digitalWrite(ledPin, HIGH); // set the LED on
delay(rate); // wait duration dependent on light level
delay(rate);
}
Chapter 2: The Basics of Arduino Programming
So far, we’ve studied what an Arduino micro-controller is, and how the
native coding environment that is provided alongside works. Let’s now step
up into learning about how actually to use all of this. This chapter will serve
to create a basis for understanding and writing Arduino code, that we can
interface into micro-controllers and create wonderful things as we so desire.
Curly Bracket
The pair of curly brackets are used to tell the IDE where a block of code
will start and where it will end; the left ( { ) for the former and the right ( } )
for the latter. You might have seen examples of this in prior sections, though
we would like to mention these are used to make code blocks other than
those of functions as well; this will be elaborated on further into this
chapter.
Do note that full pairs of brackets must exist (an equal number of left and
right brackets means that they’re “balanced”); otherwise, errors such as
crypt compiler errors will occur, in which case counting these pairs is a
good idea.
Semicolons
A semicolon is used to close off and end a statement of code (i.e., put at the
end, like a period for normal sentences) to distinguish it from other lines.
Not doing so will result in a fairly easy-to-debug error during compilation,
as the IDE will mention where this has occurred.
Another use for semicolons is separating elements in for loops, which will
also be discussed later.
Comments
Comments are bits of written text tagged with a syntax (in our case, a mix
of forwarding slashes and asterisks) so that the compiler doesn’t read them
and add them to the functionality of the program, allowing us to write
whatever we want as comments to the actual code. There are two kinds of
comments that the IDE has: Block comments and Line comments. Block
comments are usually big blocks of text that describe the code around it,
whereas line comments usually serve as a heading or a nametag for the code
near it, telling the reader what it does.
A block comment uses ‘ /* ’ and ‘ */ ‘ to define where it starts and ends,
respectively:
/* This is a block comment
This comment can span multiple lines
This type of comment is usually found outside function calls
*/
In contrast, a line comment begins with ‘ // ‘ and continues till the line ends.
This can be put at the start of a line or somewhere in between; both of these
work:
// This is a single line comment
Serial.println("Hello World"); // comment after statement
Using comments to describe your work is a very good habit for readers to
be able to discern what you want to do.
Variables
Variables are the meat and potatoes of coding. These are objects that store
information for further application by the functionality of the code. Every
variable has a unique name used to identify it and thus the information it
stores. A good habit for naming variables is using Camel Case (i.e., writing
the first word in lowercase, and then the first letter of the second word in
uppercase, like a camel’s humps, e.g., oldGreyDog, warmApplePie)
Another good habit is to initialize the variable by assigning it a value,
which helps in errors where the variable is called without a value. To do so,
we tell it what data type it will be, give it a name, use an equal sign, and
then give it an initial value (usually zero).
int myInt = 0;
Datatype
What is ‘int’? It is a datatype , a kind of badge that says what set of values
the variable can hold. Let’s examine some more built-in datatypes
commonly used in coding with the Arduino language.
Boolean
This datatype is used for basic logical decisions at least as old as
Shakespeare: true or false. This datatype‘s size is one bit. These are the only
values this set has.
boolean myBool = true;
This code declares the variable myBool as being a boolean, with an initial
value ‘true.’ As you might expect, this datatype is extremely common in
Arduino, and all comparison operations produce (or ‘return’) a Boolean
value.
Byte
A byte is eight bits, i.e., it is eight binary digits long, capable of referring to
integer values from zero or 255. The keyword for this datatype is ‘byte.’
byte myByte = 128;
Integer
By far the most used data type, we use this to store integer values; the total
number of unique values allowed is 2^16 or two bytes (sixteen binary digits
long): from –32768 to 32767. We can also have it be unsigned, via adding
the keyword ‘unsigned,’ to have this range go from 0 to 65,535. The
keyword for this datatype is ‘int.’
int mySignedInt = 25;
unsigned int myUnsignedInt = 15;
Long
Similar to the previous datatypes, the long datatype can store four bytes
(thirty-two binary digits) worth of integer numbers, from –2,147,483,648 to
2,147,483,647. ‘unsigned’ can be used as well for the same effect, going
from 0 to 4,294,967,295. The keyword for this datatype is ‘long.’
long myLong = 123,456,789;
Do note that this consumes more memory (double that of integer), so take
care to not use it unless it is necessary to store numbers that large.
Character
Characters are generally referred to as the glyphs of alphabets and various
other symbols, i.e., characters. However, on the back end, these characters
are a numerical value that refers to the ASCII chart; we can store a
character as either of these. The keyword for this datatype is ‘char’
char myChar = 'A';
char myChar = 65;
Both lines of the above code declare a character ‘myChar’ storing capital A.
We can store a group of these characters using a method that will be
discussed later, which will let us store words and sentences.
Arrays
An array is a storage structure that assigns an index value to some data and
allows us to store multiple data entries into one structure, with the caveat
that it must be of one datatype that is declared alongside the array during
initialization. A few ways to define arrays are as follows:
int myInts[10];
int myInts[] = {1, 2, 3, 4};
int myInts[8] = {2, 4, 6, 8, 10};
Notice that the name has a pair of square brackets, which is how we tell the
environment that we’re creating an array.
The first line defines an uninitialized array that can store ten
values in a row but doesn’t store any at the moment. Be careful
of declaring uninitialized arrays, as its interactions with memory
spaces can be quite weird.
The second line initializes an array with an undefined size but
with a defined number of values. In this case, the array matches
the size of the row of values that we defined.
The third line initializes an array with both a defined size and
values. In this case, the defined values are assigned to the first
spaces in the array, but the last three are not. Care must be taken
in this case for the same reason as the first declaration, which
will be demonstrated shortly.
Let’s look at how actually to use arrays:
We access a value in an array by writing the array name, the square
brackets, and the value’s index inside the bracket, such as follows:
int myInts[] = {1, 2, 3, 4};
int myInt = myInts[1]; //counting from 0, myInts contains the second
value in the array, ‘2.’
The above code notes that for most programming languages, we count
indexes starting from zero. The following should serve as further
clarification:
int myInts[] = {1, 2, 3, 4};
int myInt0 = myInts[0]; // contains 1
int myInt1 = myInts[1]; // contains 2
int myInt2 = myInts[2]; // contains 3
int myInt3 = myInts[3]; // contains 4
Now let’s look at what happens when arrays aren’t initialized. Run the
following in the Arduino IDE, and you’ll notice that the Serial Monitor
displays a jumbled mess that is definitely not integers since they were never
initialized.
int myInts[5];
Serial.println(myInts[0]);
Serial.println(myInts[1]);
Serial.println(myInts[2]);
Serial.println(myInts[3]);
Serial.println(myInts[4]);
Now let’s use what we’ve learned about arrays and characters, and combine
them into something that will let us write sentences:
Character Arrays
Character arrays are arrays that store characters. They’re initialized the
same way as regular arrays:
char myStr[10];
char myStr[8] = {'A,’ 'r,’ 'd,’ 'u,’ 'i,’ 'n,’ 'o,’ '\0'};
The first line makes an array that resizes to contain 9 characters - the word
‘Arduino’ and the null terminator, whereas the second line’s array leaves
space for them to be put in their respective positions.
Note that a string object exists, which will be discussed outside of this
chapter, but we’ll be using character arrays more often than not.
Let’s now look at something that allows us to lock a value in stone,
providing some sort of bedrock for functions to refer to and work.
Constants
A constant remains constant, i.e., any value that it is initialized with does
not change during runtime. There are two ways to declare constants; by
either using the keyword ‘const’ for it or using ‘#define.’
The first method, using the keyword ‘const,’ changes the variable's
behavior, similar to how ‘unsigned’ works. The ‘const’ keyword serves to
make the variable read-only and cause an error if there is code that tries to
change the variable during compilation. An example is as follows:
const float pi = 3.14;
We often use ‘const’ to declare constants, though we might prefer to use the
other method if memory is a concern.
The second method, using ‘#define,’ allows the user to name the constant
for use before the code is fully compiled, allowing you to reduce the
amount of memory (by skipping allocation of the name) that is required.
This trick proves useful in micro-controller models with smaller memory
sizes, such as Arduino Nano.
An important thing to keep in mind while using the latter method to declare
a constant is if the name we’re using for it is used elsewhere. For example,
some other array or variable or even another constant, the original
constant’s name will be replaced by its value instead. A good idea to
counteract this is to have completely uppercase names for constants and
regular Camel Case for other names.
‘#define’ also does not need to use a semicolon to terminate its line, as
follows:
#define LED_PIN 8
We’ve defined objects and initialized them with values. Let’s now look at
how we can use these values and work with them, using basic arithmetic.
Arithmetic Functions
The four basic arithmetic processes, summation, subtraction, multiplication,
and division, are allowed for by most if not all programming languages.
Arduino provides operators - characters that when used in code refer to
these arithmetic operations. These processes only work between operands
of the same datatype; however, an int will not add to a float. We can create
an exception to this rule using casting, which will be discussed in a short
while. For now, let’s look at how to implement these basic functions:
z = x + y; // calculates the sum of x and y
z = x - y; // calculates the difference of x and y
z = x * y; // calculates the product of x and y
z = x / y; // calculates the quotient of x and y
Sometimes, we might only need the remainder of the division (in the case
of integer operations), and we’ll use another tool for this, called the modulo
operator ( ‘%’ ). An example of how this would work is as follows: when 5
is divided by 2, the result is 2.5. If we do not go into decimal places, this
would instead return a remainder of 1. This value is the result of the modulo
operation between these two numbers.
Another nifty syntax tool we can use is compound assignment operators,
which combine the arithmetic operation with the assignment (or
reassignment) operation of the variable. Here’s a list of these operators:
x++; // increments x by 1 and assigns the result to x
x--; // decrements x by 1 and assigns the result to x
x += y; //increments x by y and assigns the result to x
x -= y; //decrement x by y and assigns the result to x
x *= y; //multiplies x and y and assigns the result to x
x /= y; //divides x and y and assigns the result to x
Comparison Operators
The Arduino language contains operator characters that allow us to make
comparisons between two objects and return a Boolean value of either one
or zero to confirm true or false, respectively. Here’s a list of those operators
in use:
x == y // returns true if x is equal to y
x != y // returns true if x is not equal to y
x > y // returns true if x is greater than y
x < y // returns true if x is less than y
x >= y // returns true if x is greater or equal to y
x <= y // returns true if x is less than or equal to y
Let’s now look at logical operators, i.e., the operators for AND, OR, and
NOT logical functions:
Logical Operators
‘AND,’ ‘OR,’ and ‘NOT’ are three operators that refer to their respective
logical operations. ‘AND’ checks whether both values (or the Boolean
results of some comparison statement) is true and returns true, ‘OR’ checks
for either value of the previous case and returns true, and ‘NOT’ returns the
opposite Boolean state of the value provided. Examples of these in action
are provided below:
(x > 5 && x < 10) // true if x is greater than 5 and less than 10
(x > 5 || x < 1) // true if x is greater than 5 or less than 1
!(x == y) // returns true if x is not equal to y
Casting
The cast operator changes the datatype of a variable to a different one,
allowing it to be used where there’s a restriction of datatypes, like for arrays
and arithmetic operations. Values converted via this operation are truncated
to the relative decimal instead of being properly rounded up or down. For
example, 8.7 being truncated to 8 instead of being rounded to 9. Thus, it is
good to cast integers to float values instead of the other way around.
Casting is done by adding parentheses before the variable to be casted and
writing the required datatype in it. An example:
int x = 5;
float y = 3.14;
float z = (float)x + y;
We need our program to make logical decisions as there’s not a lot one can
do without some sort of logic behind it. Since we’ve learned how to
compare values and draw some logical conclusion, let’s move on to using
these tools and code in decision-making capabilities.
Decision Making
Decisions are often a matter of whether something will happen or not. Most
programming languages, Arduino included, include an ‘if’ statement to that
end. This function will check a statement inside a pair of parentheses, and if
it is true(i.e., the Boolean statement returns a ‘TRUE’ Boolean value), it
will run code placed inside curly brackets, as follows:
if (condition) {
// Code to execute
}
We can also use an ‘else’ statement when the conditional statement being
checked returns false. The syntax is as follows:
if (condition) {
// Code to execute if condition is true
} else {
// Code to execute if condition is false
}
The else statement can also check a new statement and run if it returns a
‘TRUE’ Boolean value. Note that only the first block of code for which the
respective statement returns true will execute, and the rest of the else
statements will be ignored. The following code compares two variables, and
the results of that comparison allows the ’if-else’ statements to function:
if (varA == varB) {
Serial.println("varA is equal to varB");
} else if (varA > varB) {
Serial.println("varA is greater than varB");
} else {
Serial.println("varB is greater than varA");
}
‘If-else' statements can get messy and expansive fairly fast, relative to how
many statements we’d need to check. We use ‘switch/case' statements when
we have multiple conditions (more than two or three).
‘switch/case’ statements take in a value inside parentheses (here ‘var’) and
go down the list of cases, comparing the cases' value. If it finds a match, the
statement inside the case runs. After its execution, we use ‘break’ to break
the switch loop. If there is no match, a default case runs and then uses
‘break’ to end execution. The syntax for it is as follows:
switch (var) {
case match1:
// Code to execute if condition matches case
break;
case match2:
// Code to execute if condition matches case
break;
case match3:
// Code to execute if condition matches case
break;
default:
// Code to execute if condition matches case
}
Looping
There are three kinds of loops: ‘for,’ ‘while’ and ‘do/while.’ These work in
slightly different ways. Let’s start by looking at ‘for’ loops.
The ‘for’ loop continuously repeats a block of code a specific number of
times. This number is assigned to a variable that continuously changes per
iteration of the loop which can also be used to access particular indexes of
matrices.
There are three parts of a ‘for‘ loop’s statement: initialization, condition,
and increment.
for (initialization; condition; change) { }
The ‘while’ loop checks whether a statement is true or not and continues
running while it returns a Boolean true. Care must be taken with this
statement that the condition doesn’t infinitely return true, implying an
infinitely repeating loop. The syntax for this loop is as follows:
while (condition)
{
// code to execute
}
Functions
Functions are blocks of code that perform a specific task. They are given
unique names to identify them and called when needed. Programming
libraries often have hundreds if not thousands of pre-defined functions, and
we can create our own if we need to, as follows:
type name (parameters) { }
The declaration of a function requires us to start with defining what
datatype it will return. If it will not return a value, this function has a ‘void’
datatype. Moving on to naming it, use a name that describes what it does or
when it activates, such as ‘ledRedOn’ or ‘onRedCall,’ following Camel
Case.
Next, a pair of parentheses encase the parameters that we need the function
to require and use, to control its functionality. These parameters are passed
to and used by the function within its logic to return an answer respective to
the parameters. Multiple parameters can be added by separating them using
commas.
A pair of curly brackets come next, and within them, we write the code
block for the function to define its behavior.
A few examples of function syntaxes are as follows:
void myFunction() {
// Function code
}
void myFunction(int param) {
// Function code
}
int myFunction() {
// Function code
}
int myFunction(int param) {
// Function Code
}
Any variables that are initialized in the function are only usable in that
domain, and cannot be called by anything outside of it (this also means that
variables with the same names can exist uniquely inside different functions,
outside of each other’s reach). The following code block demonstrates this:
int g = 1;
void function myFunction1() {
int x1 = 2;
}
void function myFunction2() {
int x2 = 3;
}
The variable ‘g’ is accessible globally, i.e., by both functions, but ‘x1’ and
‘x2’ can only be accessed by their respective functions, not outside of these
domains.
Chapter 3: The Advanced Concepts of Arduino
Programming
Let’s now take the fundamental structures and syntaxes we’ve learned in the
previous chapter and apply them to make some more complex Arduino-
specific code structures. Do not be disheartened if coding doesn’t come to
you immediately; practice does make perfect, and you still have plenty of
chapters to go through still.
This chapter will go through the following topics:
pinMode( 11 , INPUT);
pinMode( 12 , OUTPUT);
A good habit is to never use a number directly as an argument to the ‘pin’
parameter and instead assign the value to a variable to be called for that
parameter. This helps in keeping a value constant and stopping accidental
errors. You may also use ‘#define’ to the same end.
An example of this function in action is as follows:
#define BUTTON_ONE 12
#define LED_ONE 11
void setup() {
pinMode(BUTTON_ONE, INPUT);
pinMode(LED_ONE, OUTPUT);
}
Digital Write
The function ‘digitalWrite()’ allows us to send a digital value onto the
micro-controller’s digital pin, which then can be read by some other LED or
sensor or electrical device. The syntax for it is as follows:
digitalWrite(pin, value);
The ‘pin’ parameter defines the pin to be used, while the ‘value’ parameter
is used to assign what value the pin should put out. This can be either HIGH
or LOW (or Boolean and binary equivalents), such as follows:
digitalWrite(LED_ONE, HIGH);
delay(500);
digitalWrite(LED_ONE, LOW);
delay(500);
This code makes the pin (defined by the variable LED_ONE) put out a high
voltage, wait a period of 500 milliseconds, change the pin so that it
produces a low voltage, and waits a period of 500 milliseconds again (using
the ‘delay()’ function). Let’s look at a fuller version of this code block:
#define LED_ONE 11
void setup() {
pinMode(LED_ONE, OUTPUT);
}
void loop() {
digitalWrite(LED_ONE, HIGH);
delay(500);
digitalWrite(LED_ONE, LOW);
delay(500);
}
This sets up pin 11 to send voltage from the board’s pin, as defined by the
constant’s value and then continues looping the rest of the functionality
discussed above.
Digital Read
The function ‘digitalRead()’ allows us to read what binary value is being
provided to a particular digital pin. This function's syntax consists of only
one parameter - the ‘pin,’ to define what pin to look at. Take heed of the
fact that this function returns an integer value, instead of a Boolean one,
though these values (0 and 1) are mutually interchangeable:
digitalRead(pin);
//a fuller example:
int val = digitalRead(BUTTON_ONE);
Let’s again look at a more practical example of this function in use, that
reads the status of a button:
#define BUTTON_ONE 12
void setup() {
Serial.begin(9600);
pinMode(BUTTON_ONE, INPUT);
}
void loop() {
int val = digitalRead(BUTTON_ONE);
if (val == HIGH) {
Serial.println("Button HIGH");
} else {
Serial.println("Button LOW");
}
}
Analog Read
The function ‘analogRead()’ allows us to read a voltage in an analog way,
such that every step between zero and five volts is accounted for in some
manner. The value that this function returns is an integer between 0 and
1023, meaning 1023 steps divide 5V into increments of 0.0049V. Similar to
the syntax of digitalRead(),’ ‘analogRead()’ only needs to be provided the
number of the pin it’s supposed to read. The syntax for this function is as
follows:
analogRead(pin);
After pin 5 is defined as the pin to be used and read, the loop reads the
current value the pin is receiving. It converts it to the temperature being
measured via arithmetic operations (in both Celsius and Fahrenheit), and
prints this to the Serial Monitor. A two-second delay is added between loop
iterations.
These functions will be mainstays in our coding examples and practice.
Let’s now look at code constructs to store multiple objects in a defined way.
We call these constructs Structures .
Structures
A structure is a composite, user-defined datatype that can store multiple
variables of varying datatypes. The syntax for defining this construction is
as follows:
struct name {
variable list
.
.
};
The ‘struct’ keyword precedes the name of the structure. A pair of curly
brackets stores the list of variables that we’ll keep in the structure.
In the previous section’s sketch, we used three different variables (of the
same datatype ‘double’) to define different things. Let’s try grouping them
together using a structure, and name it ‘temp_reading.’ Note again that
these datatypes do not need to be the same:
struct temp_reading {
double voltage;
double tempC;
double tempF;
};
Let’s now modify our original code so that it uses a structure and define a
function that takes this function as an argument parameter:
#define TEMP_PIN 5
struct tmp36_reading {
double voltage;
double tempC;
double tempF;
};
void setup() {
Serial.begin(9600);
}
void loop() {
struct tmp36_reading temp;
int pinValue = analogRead(TEMP_PIN);
temp.voltage = pinValue * 0.0049;
temp.tempC = (temp.voltage - .5) * 100.0;
temp.tempF = (temp.tempC * 1.8) + 32;
showTemp(temp);
delay(2000);
}
void showTemp(struct tmp36_reading temp) {
Serial.print(temp.tempC);
Serial.print(" - ");
Serial.println(temp.tempF);
}
Note the inclusion of the structure globally and a function at the end that
prints the variables inside the structure passed to it.
A structure is a good way to cut down clutter and group data, and it is a
good idea to use them to store a group of variables that contribute to closely
related actions. Another example of these groupings is as follows: the
difference between these being that only one of its variables can store a
value at any given time. These are called Unions .
Unions
This datatype allows us to store different variables of different datatypes,
similar to how structures function. However, where they diverge is in the
fact that only one of these variables is allowed to store data at a given
instance. The syntax for this construction is as follows. The only difference
between the structure’s syntax and union is the keyword:
union name {
variable list
.
.
};
Let’s look at an example of a union. We’ll create a new union (with the
name ‘some_data’) with a list of variables it can store:
union some_data {
int i;
double d;
char s[20];
};
This code creates a union (the same as before) and initializes an object,
named myData. It passes data into its variables, though printing these out
onto the Serial Monitor yields the realization that only the last variable in
the union declared is allowed to show its actual value.
‘some_data’ will only store one value at a time, shifting from the 20 of int
‘i,’ to the 3.14 of the double ‘d,’ to finally the character array storing the
value ‘Arduino’ (and its null terminator).
Before we proceed, it would be prudent to familiarize ourselves with a
useful feature of the IDE Client/Web Editor; tabs.
Adding Tabs
Tabs allow you to structure your workplace into different sections, allowing
you to divide and conquer the task at hand and also reducing your frame of
focus. Let's look at how we might add tabs:
Click on the button at the IDE's top-right, with an upside-down triangle
icon, like so:
In the drop-down menu that appears, press the New Tab option, and an
orange bar will show up allowing you to name this new tab. Press the OK
key to continue with this name and creating the tab.
After you press OK , the new tab appears:
Adding new tabs in the Web Editor is pretty much the same procedure.
Click the upside-down triangle to get a drop-down menu, click New Tab ,
put your preferred name for the tab into the space in the yellow bar, press
OK , and Voila! Your tab has been created.
A short note about how one might actually divide the code to distribute
them into tabs. For large programs, keep the main ‘setup()’ and ‘loop()’
functions in one tab, and have related functions and objects grouped into
one tab’s worth of functional table-space. Add constants globally, allowing
them to be used by multiple tabs at the same time, by including them in a
header file, which contains declarations and definitions to be used outside
it. Let’s continue.
This code defines ‘LED_ONE’ and ‘LED_TWO,’ and ensures this data is
transferred to each tab only once. This is done via ‘#ifndef’ and ‘#endif’.
The former looks at whether ‘LED_H’ is defined and allows the rest of the
code (before ‘#endif’) to run if it is not.
Once we are done dealing with the preprocessors, we can now proceed to
insert actual instructions to be executed by the compiler. These lines of code
are to be inserted in the tab named ‘led .’
void blink_led(int led) {
digitalWrite(led, HIGH);
delay(500);
digitalWrite(led, LOW);
delay(500);
}
If we analyze this small portion of code, we will see that the ‘void ’
function includes a series of parameters that control the LED’s blinking by
defining the intervals (each interval has a 500ms delay).
In the main tab, write the following to include the header file that we
defined into the sketch, using ‘#include’. Trying to refer to our constants
without using this line produces an error: “the constant was not declared in
this scope”, meaning that the compiler could not found the constant.
#include "led.h"
Header files that we write from the sketch are surrounded by double-quotes,
while header files from some other library are surrounded by less-than and
greater-than signs. This will become useful later.
Finally, we use the function that we defined in the ‘led ’ tab previously and
use it in our program’s loop. Also, you will see that in this demonstration,
we did not use the ‘#include’ preprocessor, this is because the tab we are
using does not have any header in the first place.
#include "led.h"
void setup() {
// put your setup code here, to run once:
pinMode(LED_ONE, OUTPUT);
pinMode(LED_TWO, OUTPUT);
}
void loop() {
// put your main code here, to run repeatedly:
blink_led(LED_ONE);
delay(1000);
blink_led(LED_TWO);
}
At this point, we are now ready to take this program and execute it on our
Arduino project and see the results.
Object-Oriented Programming
Object-Oriented Programming, or OOP for short, is a methodology of
coding practices that aims to break down a program into modular
component objects that interact with one another to create its functionality.
An example would be an LED object, containing variables and such to
define how we want it to work. But we need a structure or blueprint of sorts
to add these variables to, which is where classes come in.
Create two tabs and name them ‘led.cpp’ and ‘led.h.’ The former will
contain the main body of code, while the latter will contain definitions and
initializations, but also importantly the definition of our class. Add the
following to ‘led.h’:
#ifndef LED_H
#define LED_H
#define LED_ONE 3
#define LED_TWO 11
class Led{
int ledPin;
long onTime;
long offTime;
public:
Led(int pin, long on, long off);
void blinkLed();
void turnOn();
void turnOff();
};
#endif
This code is similar to what we used in the previous section’s header file.
The key difference is the addition of a class definition, containing three
variables (or ‘properties’) inside it. In order of appearance, these variables
define what pin to send data to, how long to keep the LED on, and how
long it should stay off.
Next, we use a constructor to make a class instance, and after that three
functions (or methods) are initialized.
Now, add the following block of code to ‘led.cpp,’ to give it functionality:
#include "led.h"
#include "Arduino.h"
Led::Led(int pin, long on, long off) {
ledPin = pin;
pinMode(ledPin, OUTPUT);
onTime = on;
offTime = off;
}
void Led::turnOn() {
digitalWrite(ledPin, HIGH);
}
void Led::turnOff(){
digitalWrite(ledPin, LOW);
}
void Led::blinkLed() {
this->turnOn();
delay(onTime);
this->turnOff();
delay(offTime);
}
We import two header files: ‘Arduino.h’ and our own ‘led.h’ into the code's
main body. ‘Arduino.h’ contains all the libraries and their functions that
Arduino has. It is usually automatically called, but this is not the case for
different tabs, and we need to re-initialize this header file for our new
environment.
Next up is the class constructor which is required to create a method for
initializing a class. The syntax for it is to, one, name the constructor the
same as the class, and two, separate these names via a pair of colons ( :: ).
This class contains the pin and a call to the pinMode function.
Led::turnOn() and Led::turnOff() turn the LED on or off using
digitalWrite(), and we’ll use these to define how blink_Led() works. Note
that we call functions and methods of a class using ‘->’ operators, and use
the keyword ‘this’ to specify the current instance.
Let’s now go back to the main tab and put all of this together:
Add the ‘#include’ line to include our header’s functionality, after which
you should create a globally accessible ‘Led’ class object, whose variables
we’re passing into the class constructor:
#include "led.h"
Led led(LED_ONE, 1000, 500);
String Library
One of Arduino’s basic libraries, this library allows us to work with
character arrays in an extremely user-friendly manner. Strings are more
flexible than these arrays, and allow us to work with text in a much easier
and more intuitive fashion, though it does take more memory than them.
We can create a string object in a variety of ways:
String str1 = "Arduino";
String str2 = String("Arduino");
String str3 = String('B');
String str4 = String(str2 + " is Cool");
The first and second lines simply create strings that contain the word
‘Arduino.’ The third line creates a string with one character ‘B,’ and uses a
pair of single colons to do so. The fourth line concatenates two strings,
combining them at their ends into one new string.
We can also create strings from numbers, which is allowed for by pre-built
constructors. A few examples of this are as follows:
String strNum1 = String(42); \\ return s a string ‘42’
String strNum2 = String(42, HEX); \\ returns a string ‘2a’
String strNum3 = String(42, BIN); \\ returns a string ‘101010’
Introduction
Most common motion sensors will use infrared light to track whether
something’s passed in their vicinity. These sensors, termed Passive
Infrared Sensors (PIR Sensors), detect motion in a small range around
them (usually up to two meters for simpler sensors, but industrial and
military models with a larger range and higher accuracy exist). These are
made of pyroelectric sensors which can read radiation (emitted by every
object with a temperature higher than absolute zero) in the infrared range
passively. This means that it can only read this information and not generate
anything that other devices can work off of.
The pyroelectric sensor is divided into two halves. If there is no motion,
both halves report the same amount of radiation and nothing happens, but if
there is motion (within the sensor’s range) the halves detect different
amounts of radiation, and this dissonance causes the sensor to go off.
These sensors are available in a wide variety of constructions, shapes, sizes,
and purpose-built configurations, due to their small size and power draw,
and inexpensive nature. A few examples of products that use PIR sensors
include automatic lights, holiday decorations, pest control setups, burglar
alarms, and tripwires.
As we previously mentioned, we shall be using a motion sensor IC called
HC-SR501. The following image shows the various adjustment screws and
connectors on the body of the IC.
We’ll explain what these screws and pins do, briefly. Screws act as analog
switches, able to change values in a continuous range manually from the IC
body itself, and different screws change different function variables. Pins
allow current to flow to and from the IC, allowing communication with the
Arduino motherboard. Let’s elaborate on the labeled screws and pins and
what they do.
Circuit Diagram
The Fritzing diagram for our build-to-be is as follows:
Notice the connections with the HC-SR501 IC’s power pins. The Ground
pin is connected to the breadboard’s ground rail, the 5V ping is connected
to the power rail, and the output pin is connected to a digital input pin on
the Arduino, whose respective Ground and 5V pins are also connected to
the breadboard as described. The circuit schematic for this setup is as
follows:
Code
Using the HC-SR501 sensor with Arduino is fairly simple; we only need to
read its digital state. A HIGH state indicates the sensor has ‘seen’
something (this signal, again, lasts for however long we specified via the
analog screw, which we’ll usually keep within the range of a few seconds),
and a LOW state indicates that it hasn’t seen anything yet. We’ll send forth
this status to the serial console, as follows:
#define MOTION_SENSOR 3
void setup() {
pinMode(MOTION_SENSOR, INPUT);
Serial.begin(9600);
}
void loop() {
int sensorValue = digitalRead(MOTION_SENSOR);
if (sensorValue == HIGH) {
Serial.println("Motion Detected");
}
delay(500);
}
Circuit Diagrams
The diagram shown below demonstrates the circuit diagram needed for this
topic’s project:
Instead of the 5V that we have used in the prior projects, the Nokia 5110
LCD should utilize the Arduino's 3.3V power. The 3.3V input lines on the
LCD are protected by utilizing the inline resistors. A 1K Ω (ohm) resistor is
utilized by the CE lines and the 10K Ω resistor by the rest.
The table below tells that what pins on the Nokia 5110 LCD module are
joined to what pins on Arduino:
Arduino 5110 LCD
3 RST
4 CE
5 DC
11 DIN
13 CLK
3.3V out VCC
GND BL
GND GND
To turn it off, the back-light is set to the ground. The 3.3V power out that
was utilized for the VCC pin, you can join the pin to it if you want to utilize
the back-light.
Now let’s have a look at how we can show things on the LCD:
Code
We should install two Adafruit libraries, to begin with. The two libraries are
as follows:
display.begin();
display.setContrast(40);
Now the remaining piece of code can be written in the setup ( ) function or
in the loop ( ) for the test purposes. Let us begin with lighting up only one
pixel on the display. We can do this by using the function drawPixel ( ) as
shown in the piece of code below:
display.clearDisplay();
display.drawPixel(10, 10, BLACK);
display.display();
We need to clear the buffer and the display before we draw something on
the screen. This is done by using the function clearDisplay ( ). After this, to
light up only one pixel that is located at X coordinate 10 and Y coordinate
10, we use the function drawPixel ( ). As mentioned in the previous code
we should run the display ( ) function before anything is shown on the
LCD. This is very important to keep in mind to run the function
clearDisplay ( ) prior to drawing anything on the LCD, and once we are
done drawing everything on the LCD screen to display it, we should run the
display ( ) function.
Drawing a line
Instead of calling the drawPixel ( ) function several times to draw a line, it
would be much simpler to call the function drawLine ( ) as shown in the
code below:
// draw a line
display.drawLine(3,3,30,30, BLACK);
display.display();
The function drawLine ( ) accepts 5 parameters. The first and second
parameters are the X and Y coordinates for the beginning point of the line.
The third and fourth parameters are the X and Y coordinates for the ending
point of the line. The last and fifth parameter shows the color with which
the line will be drawn. In this case, only two colors i.e., Black and White
are available as the display of Nokia 5110 LCD is monochromatic.
Displaying Text
The Adafruit library additionally makes it a lot simpler to show the text on
Nokia 5110 LCD. We can display the text by using the following code:
// Display text
display.setTextSize(1);
display.setTextColor(BLACK);
display.setCursor(0,0);
display.println("Hello, world!");
// Display Reverse Text
display.setTextColor(WHITE, BLACK);
display.println(3.14);
display.setTextSize(1);
display.setTextColor(BLACK);
display.setCursor(0,0);
display.println("Hello, world!");
display.display();
The text can be rotated counterclockwise using the function setRotation ( ).
To rotate the text 90 degrees counterclockwise we can use the value ‘1.’
Similarly, to rotate the text to 180 and 270 degrees counterclockwise we can
use the values ‘2’ and ‘3,’ respectively. The following image display the
text when the code written above is run:
Note that if the text's length is longer than the limit of what can be
displayed on a single line of the screen, the text will move to the next line.
Basic Shapes
The Adafruit library additionally makes it possible for us to make basic
shapes on the LCD. These shapes are rectangles, rounded rectangles, and
circles. There also exist different functions that allow us to make and fill
these shapes. The below-written code and images tell us the best way to
utilize the functions of a circle:
display.drawCircle(display.width()/2, display.height()/2, 6, BLACK);
Filled Shape
display.fillCircle(display.width()/2, display.height()/2, 6, BLACK);
Four parameters are required in the circle function. The first and second
parameters, the X and Y coordinates, are for the circle's center. In the
above-written codes, the center of the screen is considered as the center of
the circle. The radius of the circle is the third parameter. And the last and
the fourth parameter of the circle function is the color of the circle. This
parameter is also considered as the color to fill the circle for the function
fillCircle ( ).
Rectangle
In this section, we will look at codes that correspond to displaying different
types of rectangles. For instance, we will see how to display a rectangle that
is internally filled with pixels, a rectangle whose corners are smoothly
rounded off instead of being sharp and pointed, and finally, a rounded
corner rectangle that is internally filled with pixels.
To display a standard rectangle, we use the following code
display.drawRect(15,15,30,15,BLACK);
Filled Rectangle
To display a rectangle that is filled on the inside, we use the following code.
display.fillRect(15,15,30,15,BLACK);
Rounded Rectangle
We use the ‘drawRoundRect()’ function from the ‘display’ class and pass it
a total of five parameters for drawing a rounded rectangle. The first and
second parameters are the X and Y coordinates of the rectangle's upper left
corner. The third and fourth parameters are the X and Y coordinates of the
rectangle's lower right corner. The fifth parameter shows the color to draw
the rectangle. This last parameter is also used to fill the rectangle with the
respective color by using the fillRect ( ) function.
To display a rectangle that has rounded corners, we use the following code.
display.drawRoundRect(15,15,30,15,4,BLACK);
Filled Rounded Rectangles
display.fillRoundRect(15,15,30,15,8,BLACK);
In this +case, there are a few minor changes, we just need to include an
extra parameter to tell the code to fill in the internals of the rounded
rectangle and use the ‘fillRoundRect() ’ function instead of the
‘drawRoundRect() ’ function. The initial four parameters are similar to
those of the normal rectangle functions, the two of which are the upper left
corner coordinates. The other two are the coordinates of the lower right
corners of the rectangle. The fifth parameter tells how much to round the
rectangle’s corners and the last and sixth parameter shows the color to draw
and fill the rounded rectangle.
The examples of this chapter show that with the Nokia 5110 LCD we can
do much more than text. And the Arduino library makes it simple to utilize.
We learned to add the monochromatic display of the Nokia 5110 LCD to
put projects in this topic. These displays can extraordinarily improve the
client experience of practically any project since we can mention to the
clients what’s going on with the project and tell them if there is an issue.
Chapter 4: Structuring and Arduino
Programming
This chapter will focus a bit more on Arduino programming and structuring
the sketches we build.
void loop()
{
digitalWrite(ledPin, HIGH); // turn the LED on
for (;;)
loop();
return 0;
}
In the above sketch we see that the Arduino Hardware is initialized as at the
start of the code the function init ( ) is called. After the init ( ) function the
next function called is the setup ( ). After both these functions the last and
final function is called, that is the loop ( ). It is called repeatedly. And at last
we see that the statement ‘return’ does not run. The reason behind this is
that the for loop does not end.
void setup()
{
Serial.begin(9600);
}
void loop()
value = value - 0.1; // reduce value by 0.1 each time through the loop
if( value == 0)
else
Serial.println(value);
delay(100);
}
The calculation performed by the Floating-point numbers isn’t precise. We
can face some errors in these calculations. As we know that the numbers
expressed internally just hold estimation so these mistakes happen in the
light of the fact that a vast range of numbers falls in the category of
floating-point numbers. These mistakes bound you to check whether the
values are in a ‘range of resilience’ or not.
The following is the output from this sketch:
1.00
0.90
0.80
0.70
0.60
0.50
0.40
0.30
0.20
0.10
-0.10
-0.20
array sketch
an array of switches controls an array of LEDs
*/
int inputPins[] = {2,3,4,5}; // create an array of pins for switch inputs
}
}
void loop(){
for(int index = 0; index < 4; index++)
{
}
With values beginning at ‘0’ and finishing at ‘3,’ the loop will journey over
the index variable. This is arguably one of the most common mistakes
where sometimes an element that does not lie in the array’s dimension is
sent an access request by the user. This is an error that can have various side
effects. An approach to avoid such errors is to use a constant to set the
array’s dimension. Consider the code written below:
const int PIN_COUNT = 4; // define a constant for the number of
elements
Basic_Strings sketch
*/
String text1 = "This string";
void setup()
{
Serial.begin(9600);
Serial.print( text1);
Serial.print(" is ");
Serial.print(text1.length());
{
}
The above sketch makes two variables of type String. One is called message
and the other is called ‘anotherMessage.’ Type String variables have built-
in abilities that allow them to manipulate text. Message.length ( ) provides
(returns) the value of the number of characters (length) in the string
message.
To combine the contents of a string message.concat (anotherMessage) will
be used; in this situation, it will combine the contents of anotherMessage to
the end of the message. Here, concat is an abbreviation for concatenation.
The following will be shown on the Serial Monitor:
This string is 11 characters long.
text2 is 14 characters long.
void setup()
{
Serial.begin(9600);
}
void loop()
{
{
Serial.println( message.substring(0,commaPosition));
message = message.substring(commaPosition+1, message.length());
}
else
{ // here after the last comma is found
if(message.length() > 0)
Serial.println(message); // if there is text after the last comma, print it
}
}
while(commaPosition >=0);
delay(5000);
}
The Serial Monitor will display the following text:
Peter,Paul,Mary
Peter
Paul
This above sketch utilizes String functions to get the text from between the
commas. The line of code written below:
commaPosition = message.indexOf(,’');
finds the position (index) of the first comma used in the String named
message and sets the value of the variable commaPosition equivalent to it.
If there is no comma found in the string, the value of commaPosition will
be set to -1. If a comma is found in the string then to display the text from
the start of the string to the comma's position, the substring function is used.
The text that was displayed is taken out from the string message with the
following code:
message = message.substring(commaPosition+1, message.length());
substring results in a string having text beginning from commaPosition + 1
i.e., the next position after the comma up to the end of the string message.
This outcomes in that message having only the text after the first comma.
This is done repeatedly until no more commas are left in the string i.e.,
commaIndex will become – 1.
The low-level functions that belong to the standard C library can also be
used if you are an expert programmer. The sketch written below has the
same functionality as the previous one using Arduino strings:
/*
* SplitSplit sketch
void setup()
{
Serial.begin(9600);
}
void loop()
{
char *str;
char *p;
strncpy(stringBuffer, stringList, MAX_STRING_LEN); // copy source
string
Serial.println(stringBuffer); // show the source string
for( str = strtok_r(stringBuffer, ",", &p); // split using comma
str; // loop while str is not null
str = strtok_r(NULL, ",", &p) // get subsequent tokens
)
{
Serial.println(str);
if(strcmp(str, "Paul") == 0)
Serial.println("found Paul");
}
delay(5000);
}
The fundamental functionality originates from the function known as
strtok_r i.e., the version’s name of strtok that accompanies the Arduino
compiler. You pass the string you need to tokenize i.e., separate into
individual pieces, to the function strtok_r when you call it for the first time.
However, whenever the function strtok_r finds a new token it overwrites the
characters in this string, so as shown in the example above it's ideal to pass
a copy of the string. Every call that follows utilizes a NULL to inform the
function that it should go to the following token. In the above example,
every token is compared to a target string i.e., (“Paul”), and is printed to the
serial port.
void setup()
{
Serial.begin(9600);
}
char buffer[12]; // long data type has 11 characters (including the
// minus sign) and a terminating null
void loop()
{
long value = 12345;
ltoa(value, buffer, 10);
Serial.print( value);
Serial.print(" has ");
Serial.print(strlen(buffer));
Serial.println(" digits");
value = 123456789;
ltoa(value, buffer, 10);
Serial.print( value);
Serial.print(" has ");
Serial.print(strlen(buffer));
Serial.println(" digits");
delay(1000);
}
It is very important for the buffer being used in the sketch program to be big
enough such that it’s capable of handling every character which is being
used in the string. Seven character buffer i.e., five digits, a ‘-’ sign, and an
ending zero, for a 16 – bit integer. Twelve character buffer i.e., ten digits, a
‘-’ sign, and an ending zero, for a 32 – bit long integer. If you exceed the
limit of buffer no warning is given; and this is such a bug that can cause a
wide range of abnormal indications, as the overflow will manipulate some
other pieces of memory that might be utilized by your program. So the least
demanding approach to deal with this is to use a twelve character buffer and
always prefer to in light of the fact that it can handle both 16 – bit and 32 –
bit values.
{
if( Serial.available())
{
char ch = Serial.read();
if(index < 5 && ch >= '0' && ch <= '9'){
}
This block of code's main function is to double-check the counter’s value to
see if it’s ‘0’ or some other value and take appropriate actions. For instance,
in the scenario where the value is something other than ‘0,’ the program
blinks the LED on the Arduino board and, consequently, reduces the value
on the counter by ‘1.’ This process is repeated until the counter’s value is
brought to ‘0.’
The following is a code that accepts a parameter. It also gives some value as
a result. How many times the LED blinks i.e., it is turned on and off is
determined by this parameter. The LED keeps on blinking till we press a
button. This value is then returned:
/*
As soon as the program starts executing, the LED begins blinking.
When a ‘switch’ is paired with ‘pin 2,’ the blinking will immediately stop
after this switch is triggered.
The final result is that the program prints out the total number of blinks
the LED went through until the switch was pressed.
*/
const int ledPin = 13; // This is the LED’s output pin
const int inputPin = 2; // This is the Input pin which is connected to the
switch
void setup()
{
pinMode(ledPin, OUTPUT);
pinMode(inputPin, INPUT);
digitalWrite(inputPin,HIGH); // use internal pull-up resistor (Recipe 5.2)
Serial.begin(9600);
}
void loop()
{
Serial.println("Press and hold the switch to stop blinking");
int count = blink3(250); // blink the LED 250ms on and 250ms off
int switchVal = HIGH; //with pull-ups, this will be high when switch is up
while(switchVal == HIGH) // repeat this loop until switch is pressed
// (it will go low when pressed)
{
digitalWrite(13,HIGH);
delay(period);
digitalWrite(13,LOW);
delay(period);
result = result + 1; // increment the count
{
// implementation code goes here...
}
The next function blink2 has one parameter yet does not have any return
value:
void blink2(int count)
{
{
int percent;
val = analogRead(pin); // read the sensor (ranges from 0 to 1023)
percent = map(val,0,1023,0,100); // percent will range from 0 to 100.
return percent;
}
The name of the function is sensorPercent. A pin number is passed as a
parameter in this function and then returns a value i.e., percent. The int
written before the name of the function indicates that the function will
return an integer. While declaring functions keep in mind to select the
proper return type according to what action the function has to perform. For
the above-mentioned function, int data type is appropriate as it returns a
number between 0 and 100.
The pin is a parameter of sensorPercent. The value that is passed to the
function is assigned to the pin when the function is called.
The code that is written inside the parenthesis (the body of the function)
executes the task you need, it reads the value from an analog input pin and
afterward depicts the value to a percentage. In the previous example, the
variable named percent temporarily holds the percentage. The value stored
in the temporary variable i.e., percent is returned to the calling function
using the following statement:
return percent;
We can also get similar functionality without the utilization of a temporary
variable:
int sensorPercent(int pin)
}
The following code shows that how to call a function:
// print the percent value of 6 analog pins
Serial.print(" is ");
int val = sensorPercent(sensorPin);
Serial.print(val);
}
*/
int x; // x and y are global variables
int y;
void setup()
{
Serial.begin(9600);
}
void loop()
{
}
// swap the two global values
void swap()
{
int temp;
temp = x;
x = y;
y = temp;
}
By utilizing the global variables, the swap function changes the two values.
Global variables are available throughout the program and can be changed
by anything. Moreover, they are very easy to understand. However, they are
not used by expert programmers since it's very easy to modify the value of
these variables or have a function quit working because you altered the type
or name of this variable somewhere else in the sketch.
A more secure and more exquisite approach is to pass a reference to those
values that you want to change and let the function modify the values by
using the references. As shown below:
/*
functionReferences sketch
Serial.begin(9600);
}
void loop(){
delay(1000);
}
int temp;
temp = value1;
value1 = value2;
value2 = temp;
}
The functions having parameters that are described in the preceding
sections and the swap function both are similar. However, the symbol ‘&’
(ampersand) shows that the parameters are references. This means that the
value of the variable that is given when the function is called is changed
when the values inside the function change. This happens by first executing
the code given in the above Solution and then checking that the parameters
are swapped. Afterward, the code is modified by eliminating all the four
ampersands. Two of them are present at the top of the declaration and the
other two at the end.
The two lines that are changed should be as follows:
void swap(int value1, int value2); // functions with references must be
declared
before use
...
void swap(int value1, int value2)
By running the code, we see that the values are not swapped. The
modifications that are done inside the function are local and when the
function returns, these changes are lost.
Problem
You need to execute a piece of code if and only if a specific condition is
valid. For instance, if a switch is pressed you might need to turn on a LED
or if the value of analog is larger than the threshold.
Solution
The piece of code written below utilizes the wiring shown in the preceding
sections:
/*
Pushbutton sketch
a switch connected to pin 2 lights the LED on pin 13
*/
const int ledPin = 13; // choose the pin for the LED
const int inputPin = 2; // choose the input pin (for a pushbutton)
void setup() {
pinMode(ledPin, OUTPUT); // declare LED pin as output
pinMode(inputPin, INPUT); // declare pushbutton pin as input
}
void loop(){
int val = digitalRead(inputPin); // read input value
if (val == HIGH) // check if the input is HIGH
{
digitalWrite(ledPin, HIGH); // turn LED on if switch is pressed
}
Discussion
To test the value of digitalRead, the if statement is used. The if statement
should have a test written between the parenthesis that must be true or false.
In this given problem’s Solution, its val == High, and if the expression is
true only then the if statement is executed. A block of code comprises of all
the code written inside the parenthesis and if the parenthesis is not used,
then the block comprises of the following executable statement ended by a
semicolon.
Use the if… else statement, if you have to do a certain thing if the statement
is true and another if the statement is false:
/*
Pushbutton sketch
void setup() {
pinMode(ledPin, OUTPUT); // declare LED pin as output
}
void loop(){
}
while (analogRead(sensorPin) > 100);
The above code will cause the LED to blink at least a single time even if the
condition is false. And the LED will keep blinking until the value from
analogRead is greater than 100. The LED will blink only once if the value
is not greater than 100. A battery – circuit could be made using this code.
The LED's persistent blinking will show that the battery is fully charged
and if it blinks every other second, this will show that the circuit is active.
You need to execute at least one statement a specific number of times. The
‘while’ loop and ‘for’ loop are almost the same. However, in the ‘for’ loop,
you have more command over the beginning and terminating conditions.
In the code lines demonstrated below, the program tells the user the ‘i’
variable’s value as it went through the ‘for’ loop.
/*
ForLoop sketch
void setup() {
Serial.begin(9600);}
void loop(){
}
}
We get the following result from the above sketch:
2
3
Discussion
Generally, the ‘for’ loop consists of three portions. First is the initialization,
second is the conditional test and third is iteration i.e., an expression that is
executed toward the finish of each pass through the loop. A semicolon
distinguishes all these three parts. In the above solution's code, variable i is
initialized to ‘0’ i.e., int i = 0. The condition i.e., i < 4 checks if the variable
i is less than 4. And the iteration i.e., i ++ increments the value of the
variable i by 1 after a single iteration.
A for loop can either create a new variable that is restricted within the for
loop or it can use an existing variable. The following sketch shows the use
of an already created variable:
int j;
Serial.println(j);
}
This code is nearly similar to the previous code. However, the only
difference is in the initialization part i.e., the variable j does not have int
keyword written before it, because it is already defined outside the loop.
Bothe these codes get the same outputs as shown below:
for(j=0; i < 4; i++)
0
1
2
3
If you want to eliminate the initialization part totally you can just utilize a
variable defined before. The following code begins the loop where j has a
value equal to 1.
int j = 1;
Serial.println("for( ; j < 4; j++ )");
for( ; j < 4; j++ )
{
Serial.println(j);
}
The above code displays the following output:
for( ; j < 4; j++)
1
2
3
You control the loop termination in the conditional test. In the preceding
example, when the variable's value exceeds 4 the loop ends because the
condition is no longer true.
The code written below checks the value of the variable whether it is equal
to 4 or less than 4. It displays the numbers from 0 to 4:
Serial.println("for(int i=0; i <= 4; i++)");
for(int i=0; i <= 4; i++)
{
Serial.println(i);
}
The iteration statement that is the third part of the for loop gets executed
toward the finish of each pass through the loop. This can be any legitimate
statement of C or C++. The below given code increments the value of
variable i by 2 after each iteration:
Serial.println("for(int i=0; i < 4; i+= 2)");
for(int i=0; i < 4; i+=2)
{
Serial.println(i);
}
This code prints only two values i.e., 0 and 2.
The iteration statement can be utilized to get the values from high to low,
for example in the following code, from 3 to 0:
Serial.println("for(int i=3; i > = 0 ; i--)");
{
if(digitalRead(switchPin) == HIGH)
{
break; //exit the loop if the switch is pressed
}
Modifying Libraries
Users have the option to make changes to libraries as well if they want to
add certain functionalities needed for their projects. For instance, if we
download a library named ‘TimeAlarms ,’ we can use its methods and
classes to program an alarm on the Arduino board. However, this library
has a limitation: it only allows a total of 6 alarms to be set at any time. Let’s
say that we need more than six alarms for our project. In such a scenario, it
is much better and easier to modify the library itself rather than looking for
one that supports more than 6 alarms.
The following sketch program is using the ‘TimeAlarms ’ library. In this
demonstration, we are specifying 7 alarms instead of 6 to show the error it
will trigger.
/*
multiple_alarms sketch
has more timer repeats than the library supports out of the box -
you will need to edit the header file to enable more than 6 alarms
*/
#include <Time.h>
#include <TimeAlarms.h>
int currentSeconds = 0;
void setup()
Serial.begin(9600);
// create 7 alarm tasks
Alarm.timerRepeat(1, repeatTask1);
Alarm.timerRepeat(2, repeatTask2);
Alarm.timerRepeat(3, repeatTask3);
Alarm.timerRepeat(4, repeatTask4);
Alarm.timerRepeat(5, repeatTask5);
Alarm.timerRepeat(6, repeatTask6);
}
void repeatTask1()
{
Serial.print("task 1 ");
}
void repeatTask2()
{
Serial.print("task 2 ");
}
void repeatTask3()
Serial.print("task 3 ");
}
void repeatTask4()
{
Serial.print("task 4 ");
void repeatTask5()
{
Serial.print("task 5 ");
}
void repeatTask6()
{
Serial.print("task 6 ");
}
void repeatTask7()
{
Serial.print("task 7 ");
}
void loop()
{
if(second() != currentSeconds)
{
// the task numbers will be printed when the alarm for that task is
triggered
Serial.println();
Serial.print(second());
Serial.print("->");
currentSeconds = second();
3->task 1 task 3
4->task 1 task 2 task 4
5->task 1 task 5
6->task 1 task 2 task 3 task 6
7->task 1
#include <inttypes.h>
#include "Time.h"
#define dtNBR_ALARMS 6
If we look at the very last line, we will see the number of timer objects
defined. To add support for 7 alarms instead of 6, we simply need to change
the value assigned to ‘dtNBR_ALARMS’ accordingly. Since we only want
7 alarms, we will assign it a value of ‘7.’
#define dtNMBR_ALARMS 7
Once the modifications are done, we need to save the file to make the
changes permanent and close it. Now, we run the lines of code in the
Arduino IDE again and this time, instead of the 7th -second task being
missed, it will be properly executed this time.
1->task 1
2->task 1 task 2
3->task 1 task 3
4->task 1 task 2 task 4
5->task 1 task 5
6->task 1 task 2 task 3 task 6
7->task 1 task 7
Creating a Library
Generally, the reason why users would want even to create libraries is not to
invent new functions and capabilities within the programming language,
instead, this method is a pretty convenient way of sharing a block of code or
reusing it in other programs.
This section will create a library from a sketch program example, which
will be done without using any classes.
The sketch we will be using for this demonstration is given below;
/*
* blinkLibTest
*/
const int firstLedPin = 3; // choose the pin for each of the LEDs
const int secondLedPin = 5;
const int thirdLedPin = 6;
void setup()
{
pinMode(firstLedPin, OUTPUT); // declare LED pins as output
pinMode(secondLedPin, OUTPUT); // declare LED pins as output
pinMode(thirdLedPin, OUTPUT); // declare LED pins as output
}
void loop()
{
// flash each of the LEDs for 1000 milliseconds (1 second)
blinkLED(firstLedPin, 1000);
blinkLED(secondLedPin, 1000);
blinkLED(thirdLedPin, 1000);
}
We will now take out the ‘blinkLED() ’ function from the sketch program
and copy it over to a different file and name it ‘blinkLED.cpp .’
/* blinkLED.cpp
* simple library to light an LED for a duration given in milliseconds
*/
#include <WProgram.h> // Arduino includes
#include "blinkLED.h"
// blink the LED on the given pin for the duration in milliseconds
void blinkLED(int pin, int duration)
{
* blinkLED.h
* Library header file for BlinkLED library
*/
blinkLED(firstLedPin, BLINK_SHORT);
blinkLED(secondLedPin, BLINK_MEDIUM);
blinkLED(thirdLedPin, BLINK_LONG);
}
In this way, we assigned the constant values a variable that effectively
describes the effect of the value upon the delay in the LED's blinking lights.
When we export the sketch file to the Arduino board, we will see that the
light will blink really fast at the start, then the intervals between each blink
will be longer than before and in the final blink, the delay between the on
and off intervals will be the longest.
We can also add different functions to this library as well which were not
previously present. For instance, we can specify the number of times an
LED should blink when the code is executed. This has been demonstrated
in the following lines of code;
void loop()
{
blinkLED(firstLedPin,BLINK_SHORT,5 ); // blink 5 times
blinkLED(secondLedPin,BLINK_MEDIUM,3 ); // blink 3 times
blinkLED(thirdLedPin, BLINK_LONG); // blink once
}
If we want the library to include such functionality, we will have to make
changes to both the ‘blinkLED.h’ and ‘blinkLED.cpp’ files. First things
first, we open the header file using a text editor and include the function
prototype in it;
/*
* BlinkLED.h
* Header file for BlinkLED library
*/
const int BLINK_SHORT = 250;
const int BLINK_MEDIUM = 500;
const int BLINK_LONG = 1000;
void blinkLED(int pin, int duration);
// new function for repeat count
#include "BlinkLED.h"
// blink the LED on the given pin for the duration in milliseconds
void blinkLED(int pin, int duration)
{
digitalWrite(pin, HIGH); // turn LED on
delay(duration);
digitalWrite(pin, LOW); // turn LED off
delay(duration);
}
/* function to repeat blinking
}
}
*/
#include <WProgram.h>
#include <Print.h> // the Arduino print class
class I2CDebugClass : public Print
{
private:
int I2CAddress;
byte count;
void write(byte c);
public:
I2CDebugClass();
*/
#include <i2cDebug.h>
#include <Wire.h> // the Arduino I2C library
I2CDebugClass::I2CDebugClass()
{
}
boolean I2CDebugClass::begin(int id)
{
I2CAddress = id; // save the slave's address
Wire.begin(); // join I2C bus (address optional for master)
return true;
}
void I2CDebugClass::write(byte c)
{
if( count == 0)
{
// here if the first char in the transmission
Wire.beginTransmission(I2CAddress); // transmit to device
}
Wire.send(c);
// if the I2C buffer is full or an end of line is reached, send the data
count = 0;
}
}
I2CDebugClass i2cDebug; // Create an I2C debug object
Once we have created both the .’h’ and .’ccp’ files, we simply place them in
their appropriate folder inside the ‘libraries ’ directory. We now have
created an entirely new library from the ‘Wire library ’ and some lines of
code (to handle I2C connections). Let’s now load the following sketch
program which will be using the ‘i2cDebug library ’ to perform its core
functions.
/*
* i2cDebug
* example sketch for i2cDebug library
*/
#include <Wire.h> // the Arduino I2C library
#include <i2cDebug.h>
const int address = 4; //the address to be used by the communicating
devices
const int sensorPin = 0; // select the analog input pin for the sensor
int val; // variable to store the sensor value
void setup()
{
Serial.begin(9600);
i2cDebug.begin(address);
}
void loop()
{
// read the voltage on the pot(val ranges from 0 to 1023)
val = analogRead(sensorPin);
Serial.println(val);
i2cDebug.println(val);
}
Memory Handling
This section will discuss how to implement memory handling techniques to
ensure that our sketch program will never have to work with insufficient
RAM and sacrifice performance. Before we can even determine how much
memory we have to handle carefully, we first need to know how much
memory is even allocated to the sketch program in the first place. This can
be done by using the ‘memoryFree() ’ function as shown in the following
demonstration;
void setup()
{
Serial.begin(9600);
}
void loop()
{
Serial.print(memoryFree()); // print the free memory
Serial.print(' '); // print a space
delay(1000);
}
// variables created by the build process when compiling the sketch
extern int __bss_end;
extern void *__brkval;
// function to return the amount of free RAM
int memoryFree()
{
int freeValue;
if((int)__brkval == 0)
freeValue = ((int)&freeValue) - ((int)&__bss_end);
else
freeValue = ((int)&freeValue) - ((int)__brkval);
return freeValue;
}
This program uses the function ‘memoryFree()’ to validate the total system
memory available for use. In turn, the function performs this task by
utilizing a special type of variable known as ‘system variables .’ System
variables are not generally used in programs because the purpose of these
variables is to facilitate the IDE’s compiler in managing the internal
resources. As the program is undergoing the execution process, the number
of bytes it stores in the system memory constantly changes. Thus, it is
important to make sure that the program only uses as much memory as is
available and not more than that.
The most common elements that drain memory have been listed below;
{
int result;
result = value * 2;
return result;
}
During memory allocation. For example;
*/
#include <avr/pgmspace.h> // needed for PROGMEM
// table of exponential values
// generated for values of i from 0 to 255 -> x=round( pow( 2.0, i/32.0) -
1);
const byte table[]PROGMEM = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5,
5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7,
7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10,
10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15,
15, 15, 16, 16, 16, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 21,
22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 28, 28, 29, 30, 30,
31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 39, 40, 40, 41, 42, 43,
44, 45, 46, 47, 48, 49, 51, 52, 53, 54, 55, 56, 58, 59, 60, 62,
63, 64, 66, 67, 69, 70, 72, 73, 75, 77, 78, 80, 82, 84, 86, 88,
90, 91, 94, 96, 98, 100, 102, 104, 107, 109, 111, 114, 116, 119, 122, 124,
127, 130, 133, 136, 139, 142, 145, 148, 151, 155, 158, 161, 165, 169, 172,
176,
180, 184, 188, 192, 196, 201, 205, 210, 214, 219, 224, 229, 234, 239, 244,
250
};
const int rawLedPin = 3; // this LED is fed with raw values
const int adjustedLedPin = 5; // this LED is driven from table
int brightness = 0;
int increment = 1;
void setup()
{
// pins driven by analogWrite do not need to be declared as outputs
}
void loop()
{
if(brightness > 255)
{
delay(10); // 10ms for each step change means 2.55 secs to fade up or
down
}
When working with a large expression of values that iterate over a regular
interval, it is better to simply calculate these values before-hand and store
them in the form of an array. In this way, when the code is executed, it
doesn’t have to calculate each value repeatedly. This is not only time-
efficient, making the program to run faster, but also memory efficient as
well because if the values were to be calculated multiple times, then the
result would be stored in the system’s RAM. Instead of doing that, we
calculated the values before-hand and stored the resulting array in the
program memory, hence saving Arduino’s RAM resource from being
completely drained by this program. At the beginning of the sketch
program, we first and foremost define the table used to store the values.
This table is created through the following line;
const byte table[ ]PROGMEM = {
0, . . .
The ‘PROGMEM ’ expression is used to inform the compiler that the
values written henceforth are to be saved in the board’s program memory
rather than the board’s system memory. The values are then defined in the
form of an array. To use the ‘PROGMEM’ argument, the sketch program
needs the corresponding definitions as well, otherwise, the compiler won’t
be able to understand this instruction. For this purpose, we include a header
file with the name of ‘pgmspace.h ’ in the libraries folder and then import
it into the program using the ‘#include ’ argument.
The overall brightness of the LED light is controlled using the following
lines of code in the sketch program shown above to ensure a smooth fading
transition;
int adjustedBrightness = pgm_read_byte(&table[brightness]);
analogWrite(adjustedLedPin, adjustedBrightness);
This entire expression can also be written in the following way as well;
pgm_read_byte(table + brightness);
Let’s see another demonstration as well.
/* ir-distance_Progmem sketch
* prints distance & changes LED flash rate depending on distance from
IR sensor
*/
#include <avr/pgmspace.h> // needed when using Progmem
40,35,30,25,20,15 };
// This function reads from Program Memory at the given index
{
int value = pgm_read_word(&distanceP[index]);
return value;
}
If you recall the beginning of this book, you’ll find that we discussed a
similar experiment by using the LDR sensor instead of an Infrared Sensor.
However, the difference is that instead of measuring the light intensity
being emitted by the LED, we measure the distance of the light coming
from the LED and reaching the Infrared sensor. In the case of the LDR
experiment, we assigned different values that correspond to the different
intensities of the LED light. Similarly, we will assign values in this program
as well but these values will be stored in the board’s flash memory instead
of RAM because all of these values are constant (meaning, the values
themselves won’t change). To do this, we use the function ‘PROGMEM .’
The distance is measured and interpreted by using the function
‘getDistance() ’ and the ‘getTableEntry() function is used by the program
to fetch the corresponding values from the program memory and not from
the RAM.
int getDistance(int mV)
{
{
int index = mV / interval;
{
Serial.begin(9600);
}
void loop()
{
Serial.print(memoryFree()); // print the free memory
Serial.print(' '); // print a space
char c;
while((c = pgm_read_byte(str++)))
Serial.print(c,BYTE);
}
// variables created by the build process when compiling the sketch
{
int freeValue;
if((int)__brkval == 0)
freeValue = ((int)&freeValue) - ((int)&__bss_end);
else
}
Each character in a string takes up about one byte of memory space and
thus, a large number of strings can easily gobble up a system’s RAM very
easily. We used the same ‘PROGMEM ’ expression to move the string
constants to the system’s program memory just as how we used it to move
numeric constants in the previous section. To use this function, we need to
include the header file which includes the corresponding definitions.
#include <avr/pgmspace.h> // for progmem
Strings that are being declared in the system’s program memory have a
slightly different syntax which is as follows;
const prog_uchar myText[] = "arduino duemilanove "; //a string of 20
characters
in progmem
We can also leverage the functionality of preprocessors when declaring the
strings. This not only creates all of the strings before the code in the
program even starts executing. Moreover, the syntax used for declaring
strings by using preprocessors is easier and simpler as shown below;
#define P(name) const prog_uchar name[] PROGMEM // declare a
PROGMEM string
Once the declaration has been done using the syntax shown above, all we
have to do is use a short-expression ‘P(name) ’ and it will be automatically
be replaced by the entire string it is linked to.
P(myTextP) = "arduino duemilanove "; //a string of 20 characters in
progmem
Now let’s compare this approach to using the standard way of declaring and
storing strings in the system RAM.
char myText[] = "arduino duemilanove "; //a string of 20 characters
void setup()
{
Serial.begin(9600);
}
void loop()
{
}
Upon executing this block of code, the ‘memoryFree()’ function will tell us
that we only have a total of 20 bytes of free RAM space.
Optimizing Constant Values
This section will discuss an optimization technique that will help reduce the
memory usage of integer values. This is done by utilizing the ‘#define ’ and
‘const ’ expressions to tell the compiler the values are to be interpreted as
constants. Once the values are interpreted as constants, they can be declared
in multiple ways choosing the one that fits the situation the best.
Here's an example showing how to declare integer values as constant by
using the ‘const ’ argument.
const int ledPin=13;
If we were to declare the value traditionally, it would be like this;
int ledPin=13;
Generally, it is recommended to use constant values wherever possible
instead of just using standard integer values. Moreover, using references is
also a really good practice because you might need to change the value of
the integer at some point in program development. Then you will have to
arduously look through the entire source code and figure out the values that
also need to be adjusted when making such a change.
Three distinct approaches can define a constant integer value and each
approach has been demonstrated below;
int ledPin = 13; // a variable, but this wastes RAM
const int ledPin = 13; // a const does not use RAM
#define ledPin 13 // using a #define
// the preprocessor replaces ledPin with 13
pinMode(ledPin, OUTPUT);
By looking at this block, you will notice that the first two approaches are
quite similar at first glance but that’s not the case at all. In the first method,
we are declaring the integer as a standard variable. Since this variable can
be changed at any point in the code execution, a piece of memory is
reserved in the RAM for it. In the second method, we are using the ‘const ’
argument to declare the integer as a constant variable. In this case, since the
variable’s value is constant, this means that the value will not change during
code execution, thus the compiler doesn’t need to reserve space for this
variable in the system RAM.
Memory Handling through Conditional Compilation
There are cases when programmers keep multiple versions of the same
source code for different purposes. For instance, the source code used for
debugging will be a different version as compared to the source code being
executed as a program on the Arduino board. Similarly, some programmers
even create different versions of the same sketch program to ensure that
their program is compatible with different Arduino boards (since each
Arduino board has slightly different specifications, like system memory,
processor speed, etc.). This is an advanced practice that helps developers a
lot throughout their program’s developmental stages and debugging stages
as well.
To make this possible, we use conditionals during the compilation process.
These conditionals mainly act on the compiler’s preprocessor and directly
interfere with how the compiler builds the sketch program. Thus, we can
manipulate the way the program is built.
Let’s take a look at a sketch example. The example you are about to see is
strictly compatible with only Arduino boards that have been released after
version ‘0018.’ In other words, boards of versions ‘0018’ and older will not
support this sketch.
#if ARDUINO > 18
#include <SPI.h> // needed for Arduino versions later than 0018
#endif
Let’s now look at the sketch example which will use the ‘if’ conditional
statement to determine if we want to use the program for debugging
purposes. If the conditional is true, then the ‘DEBUG ’ function defined in
the header file will be executed.
Conclusion
We have now finally concluded our journey of exploring the world of
Arduino. Along the way, we have discovered many important methods and
techniques that advanced programmers use as their daily practice. It is very
important to polish your skills and use what you learned to understand the
concepts even more intricately. Just reading alone is not enough. Just as
how we demonstrated a practical example in every important chapter,
similarly, the reader is encouraged to practice whatever technique they have
learned along the way. The book is designed so that each chapter builds
upon the concepts explained in the preceding chapters. Hence, if the reader
does not properly pick up the crucial concepts in the beginning chapters, the
upcoming chapters will be very hard to grasp, especially the practical
portions.
We have gone through many coding techniques, methods, and practices that
are both advance and easy to use. We have learned how to effectively
manipulate strings in Arduino, use LCDs and Motion sensors, use
components like Infrared Sensors to detect the intensity of the light, and
control the blinking accordingly and this is just the tip of the iceberg. This
book is jam-packed with coding concepts and practical illustrations. We
hope that the reader found this book useful and learned a lot from it.
References:
1). Mastering Arduino by author Jon Hoffman
2). Arduino Cookbook by author Michael Margolis; ISBN: 978-0-596-
80247-9