Professional Documents
Culture Documents
Sanjib Acharya
Copyrights © 2020 Sanjib Acharya
All rights reserved. No part of this book may be reproduced, stored, or transmitted by any means-
whether auditory, graphical, mechanical, or electronic-without written permission of the author. Any
unauthorized reproduction of any part of this book is illegal and is punishable by law.
To the maximum extent permitted by law, the author and publisher disclaim all responsibility and
liability to any person, arising directly or indirectly from any person taking or not taking action based
on the information available in this publication.
From Table 2, it can be seen that Arduino® UNO is the first board
introduced in this series. In Italian, “UNO” means “ONE”, indicating the first
one in the Arduino® family. It is based on the 8-bit3 MCU ATmega328P
which has 32KB Flash memory and 2KB SRAM. The MCU ATmega328P
can have a maximum clock speed of 16MHz. These are some necessary
features based on which the other members of the Arduino® family are
compared. As it could be seen, all other Arduino® boards except the
Arduino® NANO EVERY has same sized memory – 32KB FLASH for
storing the program and 2KB SRAM for creating and manipulating variables
during execution. The Arduino® NANO EVERY, which is an enhanced
version of Arduino® NANO, has an 8-bit MCU ATMega4809. This MCU
has 50% higher FLASH memory and three times the SRAM memory
Arduino® UNO and others in the list. Also, we could use the maximum clock
speed of 20MHz, 25% higher compared to others. As the FLASH memory,
which stores the Arduino® sketch or program, is larger in memory size than
peers, the Arduino® NANO EVERY can store and execute a larger program
compared to Arduino® UNO. As the SRAM is bigger and clock speed could
be higher, this MCU could execute the same application program a bit faster.
However, it might be difficult for us to feel this without proper measurement.
Apart from FLASH and SRAM, each of these MCUs has another non-
volatile memory called EEPROM, where long term data (e.g. “board
Identification number” or “board ID”) is stored. We will go through these in
detail again in the later chapters. As a beginner, we could use any of these
boards to start our journey into the digital world. As Arduino® NANO
EVERY is the latest and has a decently powerful MCU among these entry-
level Arduino® offerings, and the price is the lowest, I would recommend
starting with that. Nevertheless, I have chosen Arduino® UNO for this book
as it stands out among the popular kick-starter platforms because of the ease
of use
There are some advanced boards available for more complex and larger
programs. An overview of those boards is given in Table 3.
The first four board in Table 3 are Arduino® MCU boards with enhanced
features. For example, Arduino® DUE is based on an MCU, which has the
highest amount of memory. These models also have a greater number of IO
pins compared to the entry-level Arduino® boards. If we have a larger
application program for which we would need more FLASH memory storage
space or require a large number of IOs, we might want to choose any of
these. Also note that, out of these four boards, Arduino® DUE is the oldest
and Arduino® MEGA 2560 REV3 is the latest. It is generally wise to choose
the latest so that the MCU board will mostly be available for a more extended
period.
2 Flash Memory (Non-Volatile): which is used to store your program. SRAM (Volatile): which is
required during execution of your program. Refer chapter 6.
3 Flash Memory (Non-Volatile): which is used to store your program. SRAM (Volatile): which is
required during execution of your program. Refer chapter 6.
4 F; Flash Memory (Non-Volatile), which is used to store your program. R: SRAM (Volatile), .which
is required during execution of our program. Refer chapter 5.
5 Only wireless communication capabilities are mentioned.
CHAPTER 3
Arduino® and friends
A) BBC Micro:bit
B) Raspberry Pi
C) ESP8266 (NodeMCU)
D) BeagleBone® Black
6 Refer https://microbit.org/
7 https://www.raspberrypi.org/
8 https://beagleboard.org/black
CHAPTER 4
Important Terminology for Beginners
n this chapter, a glossary of some of the terminology used in the rest of the
I book is given. If you are a beginner into electronics, you could scan
through it if you want. Later part of this chapter introduces the Binary and
Hexadecimal numbering systems as those two knowledge areas are essential
in digital computing, and we should undoubtedly be familiar with those
numbering systems. You could also skip this chapter now and could come
back later to refer some of these terms as you read through the other chapters.
The major reason behind using the binary numbering system in digital
computing, because the digital signals can have only two levels – HIGH (e.g.
5V) or LOW (e.g. 0V). Thus, the binary numbers 0 and 1 are used to
represent these two levels. If we want to represent only two signal levels, we
could do so using one digital signal. Now, if we want to represent more than
two signal levels, we would need greater than one number of digital signals
for that. For example, if we want to represent as a signal having ten values or
discrete states, we need four signals and can represent 0 to 9 (10 states) as
shown in Table 5.
Decimal Binary
0 0000
1 0001
2 0010
3 0011
4 0100
5 0101
6 0110
7 0111
8 1000
9 1001
Table 5: Binary Numbers Representing 0 to 9
1. Every four bits starting from LSB could be represented by one hex
digit. This four-bit aggregation is also called a “Nibble”.
2. Two hex digits could represent every eight bits starting from the LSB.
This is called one “Byte”.
3. Since, the registers in digital systems can contain 8, 16, 32 or 64 bits of
information (power of 2); it is straightforward to represent such binary
values by shorter hex digits. Thus it is easier to write codes using hex
numbers. Also, the hex number system is undeniably more convenient
compared to the decimal system in converting to binary and back.
These are the reasons why the hex number system is widely used in digital
computing and programming.
This was a quick introduction to the binary and hexadecimal system. If
you are new to this, to sharpen your newfound skill, you have to practice it
some more so that you could do binary to hex conversion or vice-versa
mentally. Going forward, we will see that the addresses of memory locations
or the register values or the variables used in the sketches are expressed in
hex numbers. It will be a bit easier for you to pick those concepts if you are
familiar to the hex system.
CHAPTER 5
Arduino® anatomy
On the other side of the Arduino® UNO board, the Digital IO header
provides connections for the UART, SPI, I2C and Digital IO functionalities
as shown in Figure 3. The default and alternative functions are described in
Table 9.
Figure 3: Digital IO Header pin description
For instance, you might buy other variants of Arduino® UNO boards such as
the Arduino® UNO SMD version as shown in the picture Figure 5 below.
This board have everything same as the Arduino® UNO R3 board except the
package of the ATmega328 microcontroller IC. You could also buy an
Arduino® Leonardo board, which is a bit different than Arduino® UNO. The
Leonardo board has a single microcontroller ATmega32U2, which does both
the functions of a USB controller and all the functions of the main
microcontroller as the ATmega328P does on Arduino® UNO. Note that the
Leonardo board also have a different USB connector than the one Arduino®
UNO board has. You would need a micro USB cable (similar to the USB
cable for phone charger) to connect and program.
Figure 5: Arduino® UNO Alternative - Arduino® UNO SMD R3 and Arduino® Leonardo R3 Boards
9 The bootloader is a program (stored in a pre-determined location of the non-volatile FLASH
memory), which accepts the application program image sent to the microcontroller over the
communication interface and stores the received program image in the destined memory locations.
10 T = (1/16000000) second = 0.0625 micro second (us) or 62.5 nano second (ns)
CHAPTER 6
Brain of Arduino® UNO
Figure 8 shows how these memories are organized and connected to the
CPU block inside the microcontroller ATmega328P.
Other than these three types of memories, we need to know about some
registers, which are part of the user data space like SRAM but not physically
implemented as SRAM.
Apart from these three main instructions, there are more such as Jump and
Call, Branch and Skip, Move, Load, Store instructions. The complete
knowledge of the entire instruction set is required for advanced programmers.
Hence this topic is intentionally kept out of the scope for this book.
There are a few more control units of the CPU that we should be aware
of. First one is a Program Counter (PC). The Program Counter (PC) of
ATmega328P is a 14-bit register. It holds the address of the next instruction
in the Program Memory (FLASH memory). The second one is an Instruction
Register (IR) which holds the current program instruction. These are the two
crucial registers we should be aware of, though we are not going to
manipulate these registers directly in our programs.
Apart from these two registers, there is another control unit in the CPU,
which decodes the instructions and generates the control signals for various
sections, peripherals of the microcontroller. This control unit is called
“Instruction Decoder”.
IO and Peripheral
Other than CPU, memory and control units, which are like core of the brain
of the microcontroller, there are IO and other special function control units,
through which, the microcontroller receives inputs and drives the desired
outputs and thus interact with its outside world.
Digital IO:
The microcontroller ATmega328P has two 8-bit IO ports, namely Port B,
Port D and a 7-bit Port C. As mentioned in Table 7, Table 8 and Table 9,
these port pins are brought out to the IO headers on the Arduino® UNO
board. We need to remember that the pin 6 of Port C (PC6) is used as the
RESET signal and the pin 6 (PB6) and pin 7 (PB7) of Port B are used for
connecting the clock signals from the clock source (which is a ceramic
resonator) on Arduino® UNO. All other port pins are used for analog inputs
or digital IO, which could also be configured to perform the alternative
functions mentioned in Table 8 and Table 9. Each port has three 8-bit control
registers inside the microcontroller, namely, DDRx, PINx and PORTx (x Є
B, C, D).
Figure 10: States of the Port Register Bit while Setting The Port Pin Input to Output or vice-versa
Interrupts:
Arduino® UNO has two external interrupt pins connected to its Digital IO
header. These are INT[0] and INT[1], connected to the Arduino® UNO pins
D2 and D3 respectively. As we have seen in Figure 6, the microcontroller
executes the program in a loop, which is in a sequence. If something crucial
is needed to be addressed in between that sequence, the interrupt feature is a
way to suspend the current program sequence execution and to perform a
higher priority task associated with that particular interrupt. That higher
priority task or function associated with the interrupt is called an Interrupt
Sub-Routine or ISR. In the ISR, we could write our program defining the
response of the microcontroller to a particular interrupt input. There are built-
in functions available in the Arduino® IDE library for defining ISR to make
things available for a quick start, and hence we will use the same.
Apart from the two external interrupts, there are interrupts generated
internal to the microcontroller ATmega328P. There are interrupts generated
by the ADC subsystem, timers/counters that the microcontroller would need
to service by executing the corresponding interrupt service routine (ISR).
Apart from the above two types of interrupts, the pin change interrupts
associated with each port on the Arduino® is also available. These are
labelled PCINT0 to PCINT23, as mentioned in Table 8 and Table 9. These
interrupts provide much simpler functionality than INT0 and INT1, as they
only trigger on a RISING or FALLING edge of the signal on an IO pin. I will
reserve this topic for a more advanced Arduino® user and will not use much
as a beginner. Thus I am keeping this topic out of the scope of this book.
Analog Inputs/ADC:
The Port C pins PC0 to PC5 could also be configured as Analog Input pins.
These six pins are brought in from the Analog IN header on the Arduino®
UNO board like it is shown in Figure 2 and discussed in Table 8. As a Digital
signal has only two states for ON/OFF conditions or HIGH/LOW levels, the
microcontroller being a digital device can read it directly. On the contrary,
analog signals cannot be read by a digital logic system directly without a
circuit translating the analog signal levels into a binary “code”. The circuit
component that converts the analog signal into digital codes is called an
Analog to Digital Converter or in short – ADC. The microcontroller
ATmega328P on Arduino® UNO has a 6-channel 10-bit ADC built-in. The
ADC converts an analog input voltage to a 10-bit digital value through
successive approximation. Therefore, it is called a Successive Approximation
Register type ADC or SAR ADC. In our program, when we set the Port C
pins 0 to 5 as analog inputs, internally, those pins get connected to the six
channels of the ADC. However, we need to make an important note here: the
ADC is a single channel ADC. These six channels are multiplexed; means we
could select one channel at a time. Thus, we could only read one channel at a
point in time. The analog input channel is selected by writing to the MUX
bits in ADMUX register and control the ADC using ADC Control and Status
register ADCSRA.
An ADC can convert a signal only if the value of the signal falls in the
maximum range or the full-scale range, for which the ADC is designed. If we
provide a voltage outside its range, it will convert it into a wrong code, or it
could get damaged. Hence, we should ensure that our input signal always
falls inside the full-scale range (FS range) of the ADC. The ADC of the
ATmega328P controller has a configurable range. This full-scale range is
configured by setting the appropriate voltage reference for the ADC. For
ATmega328P, we could either select AVCC or the internal 1.1V reference or
an external reference to the AREF pin of the Arduino®.
If we select AVCC as the reference for the ADC, which is the default
setting, the signal voltage shall be within the range 0V (GND) to 5V
maximum. As the ADC inside ATmega328P is a 10-bit one, it will convert
this analog range of 0-5V into the corresponding binary codes ranging from 0
to (210 – 1) where 0V signal will have the integer value 0 or binary value
“0000000000” (hex: 0x000). On the other side of the range, 5V will be read
as the integer value (210 – 1), i.e. 1023 or binary value “1111111111”
(0x3FF). The ADC generates a 10-bit result which is presented in the ADC
data registers, ADCH and ADCL. By default, the converted result is “right
adjusted”. That means, the registers ADCL and ADCH would contain the
lower 8 bits and the upper two bits respectively.
If the voltage of a channel exceeds VREF, then the ADC conversion
result would be a code close to 0x3FF (Full-scale maximum). Nevertheless,
we must not apply a voltage larger than 5V to the ADC input as that might
damage the microcontroller and the Arduino® UNO board. In case, the
analog signal falls within a tiny range of 0-1.1V, we should select the 1.1V
internal reference using the REFS0 and REFS1 bits of the ADMUX register.
In that case, the analog range of 0-1.1V will be mapped to the digital or
binary range of 0-1023 count (0x000 to 0x3FF).
We have a third choice of using the external reference voltage AREF. If
we select AREF to be the input for the ADC reference, we should connect a
DC voltage to the AREF pin. That shall be in the range of 1.0V to AVCC
(5V for Arduino® UNO). We should use this configuration for measuring an
analog signal falling in a range, say 0.5V to 3.5V and for that, we would need
to apply 4V to AREF pin. The voltage reference selection and the
corresponding full-scale range is depicted in Figure 11.
Figure 12: ADC Registers and Their Connection To The Internal Data Bus
The ADC also needs a clock. There is no external ADC clock input on the
chip, but the required clock is generated internally from the same clock used
to run the microcontroller. By default, the ADC of AT-mega328P requires an
input clock frequency between 50kHz and 200KHz to get the maximum
resolution of 10 bits. The CPU clock is faster, and hence an adjustable
“Prescaler” is needed to divide the CPU clock frequency for generating a
slower ADC clock. The Prescaler can be any choice of divisors among 2, 4,
8, 16, 32, 64, or 128 and it is up to us to select one that results in an ADC
clock within the acceptable range.
Let us now get familiar with the ADC registers. An advanced Arduino®
user should always refer to the datasheet or the user manual document of the
microcontroller, which is the right place to find all the required information.
As I have kept the scope of this book limited to beginners, I have attempted
to provide just enough information for my readers to develop a certain level
of acquaintance without the need to refer the datasheet.
A. ADMUX Register:
The ADMUX register controls:
The reference voltage setting for the ADC for controlling the full-scale
range.
The input multiplexer for selecting one of the six input channels.
The modes for arranging the conversion results in the ADCH and
ADCL registers.
7 6 5 4 3 2 1 0
REFS1 REFS0 ADLAR -- MUX3 MUX2 MUX1 MUX0
Table 11: ADMUX Register Bits
Four LSBs of the register ADMUX, MUX3 down to MUX0 control the
selection of one of the six input channels of the ADC, as shown in Table 12.
The ADLAR bit determines how to arrange the converted result in the
ADCH and ADCL registers. We will use the default setting for the right
adjusted result and let us ignore the other setting for now.
The last two bits REFS1 and REFS0 control the selection of the reference
voltage source for the ADC as shown in Table 14. This setting determines the
full-scale range of the ADC.
7 6 5 4 3 2 1 0
ADEN ADSC ADATE ADIF ADIE ADPS2 ADPS1 ADPS0
Table 15: ADCSRA Register Bits
Bit 2 down to Bit 0 are the Prescaler bits those set the divisor for the
ADC clock.
ADPS2 ADPS1 ADPS0 Prescaler
Value
0 0 0 2
0 0 1 2
0 1 0 4
0 1 1 8
1 0 0 16
1 0 1 32
1 1 0 64
1 1 1 128
Table 16: ADC Prescaler Bits
If ADLAR is set to ‘1’, then the result will be “left-adjusted” like shown
in Table 18. The advantage is we could just use the value in the ADCH
register and ignore the last two LSBs stored in the ADCL register. However,
the converted result will have an 8-bit resolution instead of 10-bit.
ADCH 7 6 5 4 3 2 1 0
ADC9 ADC8 ADC7 ADC6 ADC5 ADC4 ADC3 ADC2
ADCL 7 6 5 4 3 2 1 0
ADC1 ADC0 -- -- -- -- -- --
Table 18: ADCH and ADCL Registers (Left Adjusted) When ADLAR = ‘1’
These are the essential registers for setting up the ADC using
programming. We might not need to use or modify any of these registers
directly by ourselves as the functions available in the Arduino® library will
do all that. Instead, we need to use those functions in our code. We just need
to be aware of what happens in the background. In Chapter 11, we will see
the use of analogRead() function, which will configure all these registers for
you.
Timers/Counters:
The next function we are going to learn about is Timer & Counter. This is
another critical function. There is a lot to know if we want to utilize this
feature to the full extent. To begin making, we might not need to know a
whole lot. In this section, I have highlighted some of these “need to know”
aspects of Timer/Counter. ATmega328P contains three timers or counters:
Timer/Counter0, 1 and 2, as shown in Table 19 below. Please note that each
timer has two output channels A and B.
The timer registers are listed in Table 20. The TCNTn register contains
the counter value for the timer. Note that ‘n’ denotes the corresponding
number of the Timer0, 1 or 2. For the ease of describing, I am going to use
‘n’ in the nomenclature of the timer/counter registers or any particular bit of a
register to denote the respective number of the timer. When the timer runs,
the control logic increments the TCNTn register by one on each “clock tick”.
As discussed before, the timer “clock tick” could be different from the
Arduino® UNO’s “clock tick” depending on the Prescaler bits setting in the
control registers TCCRnA/B/C.
TCCRnA/B/C registers are the control registers of the respective timers.
As we could see in Table 20, Timer0 and Timer2 have two 8-bit control
registers each, while Timer1 has three 8-bit control registers. By setting the
bits of the control registers of a timer ‘1’ or ‘0’, we could configure the
corresponding timer for a mode. In the following section, we will see two
basic configurations of a Timer: mode selection and configuring the
Prescaler. Using individual bits of the timer/counter control registers, we
could configure the mode in which the timer would operate and with other
bits select the clock Prescaler.
Prescaler Selection: A Prescaler dictates the speed of our timer according
to the following equation:
The Prescaler for a timer is determined by the three LSBs of the register
TCCRnB. These bits are CSn2, CSn1 and CSn0. Three bits could generate 23
= 8 possible values for the Prescaler. Among these three timers, only Timer2
uses all eight values for Prescaler. Timer0 and Timer1 use six combinations
of CSn bits for configuring the Prescaler, and last two combinations are used
for selecting the external clock for the timer. The Table shows the use of the
CSn bits for the three timers.
Figure 13: Illustration of Counter Value Incrementing in TCNT Register With Timer Clock
Communication Interfaces:
Another primary function of a microcontroller is communication. The
communication interface of the microcontroller enables other intelligent
devices such as another microcontroller, personal computer to send and
receive meaningful information in the form of digital data (bits: ‘1’s and
‘0’s). Two types of communication interfaces are available – parallel
interface and serial interface.
The parallel communication needs a data bus of width 4, 8, 16, 32, 64 or
more. The concept of parallel interface is simple: we arrange the information
to be sent in arrays or packets of the same size as the data bus and send all
those bits in the packet at the same time on a single clock edge. If the parallel
interface has a width of 8 bits, we could send 8-bit wide arrays of information
on each clock edge, as shown in Figure 16. However, in this type of
communication, we need a larger number of signal pins compared to the
same for serial communication. For example, we need eight signal pins for
sending 8-bit data or 64 signal lines for sending 64-bit data apart from the
clock and control signals.
Figure 16: Data Flow with respect to the Clock Edges on 8-Bit Parallel Communication Interface
Figure 17: Data Flow with respect to the Clock Edges on a Serial Communication Interface
e have the sensory system to see, hear, feel hot or cold when we touch
W something and experience our surroundings for making informed
decisions. Likewise, a microcontroller also can use electronic sensors
and could be programmed to perform actions based on specific input signals
from the sensors connected to it. The sensors are the devices which measure
the physical parameters and convert those into the electrical signal the
microcontrollers could read. To build an intelligent machine such as a robot,
we need to use several such sensors. The more intelligence we want to pack
in our gadget, more numbers of different sensors are needed to be connected
to our device.
The sensors convert physical parameters into electrical signals. Thus, we
need to know what type of signal is produced by a particular sensor before
connecting the same to the Arduino® UNO board. We have already seen that
the Arduino® UNO could accept only the following types of signals – (A)
Digital (High or Low), (B) Analog (0 – 5V maximum). Apart from the
signals, we could also read the measured values through any of the serial
communication interfaces such as SPI or I2C or USART supported by the
sensor. We need to know what maximum voltage the sensor can produce. If
the voltage is higher than 5V, we shall not connect that signal directly to the
port pin of the Arduino® UNO as a voltage higher than 5.5V might damage
the port pin of the microcontroller and the board. Hence we need to double-
check whether the sensor is compatible with the Arduino® UNO or if we
need an intermediate circuit component.
Buttons and Switches: The switches and buttons act as the input devices
to the microcontrollers as “user interfaces” (UI). There are special types of
switches specific to different applications. For example, a magnetic reed
switch closes or opens its contacts influenced by the presence or absence of a
magnetic field in its proximity. Therefore, this type of switch is used as a
proximity sensor. Another type is a contact switch or commonly known as
the “limit switch”. It is used to detect the limiting distance till which a
mechanism could move and touch the lever of the switch. Subsequently, the
switch position change is detected, and the movement could be stopped. The
application of limit switches could be seen in the elevator system of a
building. Once the elevator hits a sensor while moving up or down, the
elevator’s controller senses the position of the elevator and takes appropriate
actions such as stopping at the destined floor or displaying information about
the floor it passes by.
The switches and buttons have contacts which either close to allow the
flow of current through a circuit or open to stop the flow of current through
the circuit. Depending on the state of the contact, whether opened or closed, a
signal having two digital states such as HIGH or LOW, could be generated.
The microcontroller can read this signal as a digital input. Figure 20 shows
how we should connect a switch or a button to a digital IO of Arduino® UNO
or any other microcontroller board for reading the state of the contact. When
the contact is open, the current does not flow through the switch. Hence, the
voltage on the DIO pin becomes 5V as there is a pull-up resistor connected to
the 5V supply. When the switch is closed, the voltage on the DIO changes to
0V as the DIO pin gets shorted to ground (GND) through the switch. Thus,
the microcontroller on Arduino® reads ‘1’ or HIGH when the switch is OFF
and reads ‘0’ or LOW when the switch is ON. Practically, a signal state
change from HIGH to LOW or the vice-versa does not happen
instantaneously. As the switch or the button is pressed, the contact
“debounces” due to spring action. That means, the switch randomly turns on
and off for several milliseconds. This phenomenon is called contact
debounce. As the microcontroller can read much faster than a millisecond,
during “debounce time” it will read ‘1’s and ‘0’s as the contact debounces.
The debouncing results in an incorrect signal that we need to filter. As a
hardware solution, a debounce filter capacitor could be connected in parallel
to the switch contact like it is shown in Figure 20. The functions such as
delay() or millis() could be used to wait after a change detected before
checking if the last change was stable. The pull-up resistor and the capacitor
will form a filter, which will ignore the fast change in the signal. Hence, the
input signal transition from 5V to 0V will be read correctly. Alternatively, a
software solution could be to write a “debounce filter algorithm” in the
program. The functions such as delay() or millis() could be used to wait after
a change detected before checking if the last change was stable. However,
this would need a bit more experience in programming. Hence, connecting a
debounce capacitor is always useful. As a thumb rule, we could connect 10uF
debounce capacitor with a 10 KΩ pull-up resistor. We do not need to bother
much as the digitalRead() function that we will use to read the state of a
button has the debounce filter implemented.
The other kind of sensor module comes with an additional amplifier and
associated circuit, which converts the sound energy level into an analog
signal proportional to the sound level. This signal generated by this type of
module is an analog type and needed to be connected to the analog input port
pin of the Arduino® UNO board. The ATmega328P microcontroller reads the
sound levels as count values using the analogRead() function. This type of
modules could be used for measuring the sound pollution level.
Ambient light sensor: Ambient light sensors are the photosensors, which
are sensitive only to the visible spectrum of light. Hence, the ambient light
sensors could closely imitate the human eye response to visible light. As we
know, light is an electromagnetic wave. Different colours in the visible light
spectrum have different wavelengths, as shown in Figure 22. The ambient
photo sensors are responsive only to the visible light spectrum and sense
ambient light closely similar to as human eye sees the light.
Before we learn more about the sensors, let us get a little familiar with
some of the terms which are used to measure light.
Figure 25: Touch Sensor Modules, Those could be Used with Arduino
The touch sensor modules usually have the I2C interface for the
Arduino® to read the status of whether someone touched. The screens of our
smartphones have a touch sensor.
Accelerometer Sensor: The accelerometer sensor measures the
acceleration along with one or multi directions. The sensor which measures
the angular acceleration on one axis is called Gyroscope. The GY521
gyroscope module for Arduino® is based on a chip MPU6050. The MPU6050
IC consists of three independent gyroscopes, which detect rotation about the
X-, Y-, and Z- Axes. When the gyros are rotated about any of the sense axes,
the “Coriolis Effect” causes a vibration that is detected by a capacitive
pickoff. The resulting signal is amplified, demodulated, and filtered to
produce a voltage that is proportional to the angular rate. This voltage is
digitized using individual on-chip 16-bit Analog-to-Digital Converter (ADC)
to sample each axis. The GY521 module has the I2C interface using which
Arduino® can configure the MPU6050 IC and access the data.
Figure 26: Accelerometer Module Based on MCU6050
Distance (in cm) = [0.0343 * time lag (in microseconds)]/2 = time lag (in
microseconds) / 58
The popular sonar module is HC-SR04, which has both the transmitter and
the receiver, as shown in Figure 27. The transmitter is marked with “T”, and
the receiver is marked with “R”. The module operates with 5V and has two
signal lines – “Trig” and “Echo”. When the microcontroller on the Arduino®
UNO sends a pulse of width 10us to the “Trig” pin of the HC-SR04 module,
the module sends eight ultrasound pulses of ∼40KHz in the forward direction
with an angle of ±45◦. The most effective angle in which the HC-SR04 works
best is ±30◦.
The chip on the module calculates the distance of any object from which
the echo reflects. Next, the HC-SR04 module drives a pulse on the “Echo”
pin. The width of the pulse varies between 150us and 25ms according to the
distance of the object. This width of the echo pulse is the “time lag” (in
microseconds) in the equation explained above. If the pulse width is
anywhere between 150us and 25ms, we could get the distance in cm of the
object by dividing the pulse width in microseconds with 58. If the “Echo”
pulse width is 38ms, that indicates that the HC-SR04 module did not find an
object ahead. The signal “Trig” can be a digital output from Arduino®, and
we could connect the “Echo” signal to a digital input pin of Arduino®. We
would also need to connect the 5V and GND from Arduino® to power the
module up.
IR Sensor: The IR sensor modules available in the market are based on
Infra-Red sensor (looks like an LED) and the IC393, which is a comparator.
The infrared spectrum has longer wavelengths than the highest wavelength
(∼750nm for RED) in the visible light spectrum. The infrared (IR) spectrum
is shown in Figure 22, works in the 760nm – 1100nm wavelength range.
Figure 28: 760nm - 1100nm IR Infrared Flame Sensor Module For Arduino
The detection angle of the module is about 60◦. As the flame has some
infrared content too, this sensor module also could be used as a flame sensor
for a maximum sensing distance of 0.8 meters. The accuracy of detection
could be adjusted using the potentiometer shown in Figure 28. There are two
output signals available from this module – analog output and digital output.
The digital output indicates whether flame/IR is detected or not. The analog
output provides a voltage proportional to the intensity of the IR/flame
detected by the sensor. We could connect either or both to Arduino®
depending on our application.
There could be several applications of the IR sensor. Some of those are –
IR remote control, proximity sensor, flame sensor, motion sensor, thermal IR
sensor, burglar alarm – human body detection and likewise.
There are countless other types of sensors available, but these are the
primary sensors that we should know to start with our experiments.
11 There are other models having different specifications available and this series of ICs are referred
as SHT3x by the module manufacturers. Hence please check which model is on the module and refer
specification of the same.
CHAPTER 8
Output Devices
There is a third type of relay, which has change-over contacts. This type
of relay is called FORM C type. When the coil of a Form C relay is
energized, the NC contact breaks and NO contact makes, and a change-over
happens. In a UPS, we have heard this sound of the relay changing over
between the mains power and battery.
The electromechanical relays are very popular and widely used, despite
being an older technology. The solid-state relay (SSR) is a relatively newer
technology, which is gaining popularity and displacing electromechanical
relays in many applications. Even though the initial cost of SSR is higher
compared to the cheaper electro-mechanical counterparts, SSRs have more
durability and reliability and in the long term. The solid-state relays do not
have the mechanical contacts for switching. Instead, they have semiconductor
switches such as transistors or triacs. The control of the switching is usually
provided using optical diodes or LEDs built into the SSR, as shown in Figure
32.
The motors need a larger current than the microcontroller’s pin could
supply. Thus, we need to use a motor driver shield which can supply the
current needed for driving the motor and can be controlled by the
microcontroller.
Figure 35: BLDC Motor Shield For Arduino (source www.infineon.com)
Figure 36: 8x8 Dot Matrix Display With MAX7219 Driver (Source: instructables.com)
As shown in Figure 37, eight LEDs arranged in a row has the anode (+)
terminals of the LEDs are connected. Those eight anode connections of the
eight rows are labelled with letters A, B, C, D, E, F, G and H. On the other
hand, the common cathode (-) terminals for eight columns of LEDs are
labelled with the numbers 1 to 8. Now, we would need only 16 IOs altogether
to drive 64 LEDs, which is four times lower. However, for a small
microcontroller like ATmega328P, reserving 16 IO pins for driving only
LEDs can not be afforded, and the current driving requirements is still an
issue. To solve this problem, ICs such as MAX7219, which has 24 IO pins
and technique improvised based on “Persistence of vision” could be used by
Arduino® to make the LEDs appeared to be permanently ON whereas those
are not driven continuously. Our eyes cannot see a quick blink which happens
as fast as 20 milliseconds or quicker, and this is called persistence of vision.
So, if we turn an LED ON, then turn it OFF for less than 20 ms and again
turn it ON, to our eyes the LED will appear to be ON all the time. Our eyes
will miss a blink. We could use this technique in driving the LEDs in the dot
matrix array one at a time with a quick blink and can save on driving current
& power requirements. On the other hand, the Arduino® needs only three of
its IO pins for driving the Data (DIN), Clock (CLK) and Chip Select (CS)
signals of the MAX7219 IC on the LED Dot Matrix module as shown in
Figure 36. As we have discussed in Chapter 6, this is the SPI interface.
LCD Display: LCD stands for Liquid Crystal Display, another technology
based on which displays are made. We just need to know that the characters
in the LCD are made of pixel matrixes, on which letters or numbers could be
imprinted by turning the respective pixels on. The concept behind displaying
on a pixel matrix is very similar to displaying a character on the LED dot
matrix display. Arduino® only needs to send appropriate data on the signal
pins of the display and the driver IC on the LCD takes care in displaying the
corresponding character on the pixel matrix of the display.
Figure 40: 16x2 LCD Display And The Breadboard Layout Connections With Arduino
The display shown in Figure 40 is a 16x2 display. There are two rows of
16 characters in each of the rows. Each character is formed by 5x7 pixel
matrix. This is very small, isn’t it? There are graphical LCDs available, in
which one large grid of pixels (e.g. 128x64) could accommodate much larger
sized characters or some simple graphical symbols. As we go for bigger
LCDs with more pixels, we would need to pay more. The 16x2 LCD has
eleven signal lines. There are eight data lines D0 through D7 and three
control lines such as RS, EN, RW. We will revisit these signals when we
program the Arduino® to display a message on an LCD.
OLED Display: Organic Light Emitting Diode or OLED is a relatively
newer technology compared to the LCD technology. Nowadays, different
varieties of OLED displays are used on smartphones. An LCD has certain
limitations, which are overcome with OLED technology. For example, we
cannot see the characters displayed on an LCD screen in the dark. We will
need a back-light for illuminating the screen. The display of a calculator is an
LCD. We have experienced that we cannot read the display in the dark
without the backlight. The OLED, on the other hand, does not need a
backlight as it is easily visible in the dark. Another issue with the LCD is that
the viewing angle is very narrow. If we cannot get a seat directly in front of
the TV with an LCD screen, we might not be able to see the picture correctly.
For OLED display, the viewing angle is wider than the same on an LCD.
Hence, OLED is more in use nowadays.
One of the commonly available OLED displays for Arduino® is a 0.96-
inch display with 128x4 pixels, as shown in the figure. At the time of writing,
the part number of the 0.96-inch OLED display is SSD1306 from “Solomon
Systech”. If interested, one could download the datasheet and read through it
to learn more about the display. This display has an I2C interface, using
which the Arduino® could talk to it.
Table 26 captures the pin connections between the SSD1306 display and
the Arduino® UNO board. The signals SCL and SDA are the clocks and the
data signals for the I2C interface. As multiple slaves could be connected on
the I2C bus, we could connect multiple displays on the I2C bus with the
Arduino®. The code library for “Adafruit SSD1306” (By Adafruit) is also
readily available for download and use.
E-paper Display: The E-paper display technology is still evolving. The
Kindle users have already experienced the beauty of the reading from an E-
paper display. The E-paper display works based on “E-ink” technology. The
significant advantage of the E-paper display is that it consumes meagre
power compared to its other display counterparts. The reason behind a low
power consumption is that the E-paper does not need the power to maintain
the image being displayed. It will need power when it needs to change the
image. The major drawback is that the image displayed on an E-paper cannot
be changed at a fast rate. Therefore, a video cannot be viewed on an E-paper
display. It is challenging to find a coloured E-paper display.
Nevertheless, the E-paper display has its applications where the image
displayed is stable for a longer duration, such as e-book (Kindle). There are
some small E-paper displays available, which could be easily connected to
the Arduino®. These displays have SPI or I2C interface using which the
Arduino® board could send data to be displayed. However, the E-paper
displays are mostly black-and-White, and these are still a bit expensive.
So far, we have learned about various sensors and output devices. Next,
we will learn how we could connect these components to the Arduino® UNO
board and how we could program the Arduino® board to build an application.
CHAPTER 9
Building Circuit on Breadboard
The Breadboard shown in Figure 41 has 400 tie points consisting of 300
tie points on one terminal strip and 100 tie points on two distribution strips.
The dimension of this Breadboard is 3.2”x2.1”. There are other models of the
Breadboards available with a higher number of tie points per terminal strips
and multiple terminal strips and distribution strips. Nevertheless, the 400 tie
points should be good enough for a beginner to start with smaller circuits.
The layout of a 400-tie point Breadboard is shown in Figure 42. There are
two columns of connections on each side along the longer length of the
Breadboard. One column of tie-points is marked with “+’ and the other
column is marked with “-“. All the tie-points in a column, marked with “+”
are internally electrically connected. The same is for the tie-points marked
with “-“. The tie-points connected are shown in Figure 42 by the lines drawn
through them. Each of these “+” and “-” columns are made out of one single
metal strip. This connection is hidden inside the plastic cover and appear as
separate points. Therefore, all the “+” tie points are electrically shorted. The
same applies to the “-” tie-points. These two columns of tie-points are meant
for connecting the voltage rails for supplying the power needed for the
circuit. For example, connect 5V to “+” and GND to “-“. If we need another
voltage such as 3.3V, we need to use the other power distribution strip.
The terminal strips in the middle are arranged in rows. Each row of a 400-
point breadboard has two separate strips. In each of the thirty rows, five tie-
points marked as ‘a’, ‘b’, ‘c’, ‘d’ and ‘e’ are electrically shorted as these
points are made out of a single metal strip. The same is true for the other five
tie-points marked as ‘f’, ‘g’, ‘h’, ‘i’ and ‘j’. Each of these five tie-points in a
row marked with letters a-to-e or f-to-j can be imagined as a circuit node
where the wires are connected. In Figure 43, a circuit node is replicated on
the breadboard. In this circuit node, one terminal each of the three
pushbuttons are connected to the positive terminal of the 9V battery. As
shown in Figure 43, the row 10, tie-points a-to-e replicates the same circuit
node as shown by the orange coloured wire on the circuit on the left side.
Now, let us see how we should come up with our circuit on the
breadboard. Let us get ahead with a simple circuit shown in Figure 44. A
push-button and a resistor form an input circuit such that when the button is
pressed, the pin D10 of Arduino® UNO will be pulled LOW. The signal
remains HIGH when the button is not pressed. The code running in Arduino®
detects a LOW signal on D10, when the button is pressed and drives the pin
D7 LOW to turn on the LED connected to the same pin.
In Figure 44, each piece of the wires indicates a circuit node. The wires
are electrically shorted together in each node. For example, the wire between
the 5V and the two resistors denotes the “5V” node. The wire connected from
the push button to the GND pin on Arduino® indicates the “GND” (Ground)
node. The input is connected from the node where the 10K ohm resistor and
the push-button are connected. This wire is connected to D10 of the
Arduino® UNO. The wire from the D7 pin of Arduino® is the output node
which drives the LED.
Figure 44: A simple Input & Output Circuit Using Arduino UNO
Figure 46: Starting A New Wiring Diagram In The “Circuits” Tool On Tinkercad
With this overview, let us get on building the circuit described in Figure
44, using the virtual breadboard, Arduino® UNO. Once the breadboard circuit
is completed, the simulation could be the next step provided we have a
working code. There are two main options available for coding: either we
could code using the supported programming languages, or we could use
“Code Blocks”. For a new programmer, the “Code Blocks” option is helpful.
However, beginners should be encouraged to pick up the programming
language instead of using the graphical programming blocks. Coding using a
language would be much more flexible, compact, and we will get many
reference codes available on the net, for us to get started. Figure 47 shows the
coding example using “Blocks”. As shown in Figure 48, if we click on the
“Code” tab on the toolbar, the code window will open. Here, we could type
our code or copy and paste a working code.
After we complete drawing our circuit and adding code, we would click
on the “Start Simulation” button to simulate what we have just built. Once we
simulate our circuit successfully, we should be able to replicate the same on
the breadboard physically, and it should work. If our simulation does not
show expected results, we need debug. Remember that the issue could be
there in the code or the circuit connection. Hence we need to simplify our
circuit first, to check if the basics are working. Debugging is a step-by-step
process of simplifying a complex circuit or code, into a very basic one. We
need to make the minimum features work and then, would continue adding
changes one at a time while checking in each step unless the bug is found.
We need to continue the incremental change and test to reach our desired
circuit configuration.
Figure 48: Adding The Code For This Project
Figure 50 shows the simulated wiring of the circuit that was described in
Figure 44. The working code for this circuit is shown in Figure 49. We will
learn about coding in the next few chapters, but the readers could always try
this out in the code section of the tool along with the breadboard circuit to
simulate the connections.
Figure 49: Working Code For the Push-Button and LED Circuit
Figure 50: Breadboard Wiring Diagram Of the Push-Button and LED circuit
CHAPTER 10
Getting Started with Programming
To open the example “Blink” code that comes with the Arduino® IDE
installation, click on File → Examples → 01.Basic and there we will find the
“Blink” code. It could also be observed that other folder containing the
example code such as “02.Digital”, “03.Analog,” “04.Communication”,
“05.Control”, “06.Sensors”, “07.Display” and so on are also made available
for us to explore. We will use some of those sketches in the subsequent
chapters.
pinMode(LED_BUILTIN, OUTPUT);
Next, we will make a small change in the “Blink” code. In the default
sketch, the value of the delay() function is set as 1000, which is in a
millisecond. We will change this value to 200 in both the places in the sketch.
This change would alter the delay between successive OFF and ON from 1
second to “0.2 seconds”. With this change we expect to see the LED to blink
at a faster rate, isn’t it? After we make the change, as shown in Figure 60, we
need to save the change by going to File → Save menu. Next, we need to
compile and upload the sketch by hitting the “Upload” button. We could also
use the Sketch → Upload menu. After the Arduino® software compiles our
code and uploads the program into the Arduino® UNO board, we would then
see the LED blinking at a faster rate.
We could play with the values further, but as we have progressed so far,
we are now all set to start building our code. However, before we go ahead,
let us get ourselves familiar with some of the C/C++ terminologies.
In this book, as I have focused on proving an overview on each of the
topics that could enable the beginners to start their learning journey into the
world of electronics, open-source microcontroller boards and programming
using Arduino®. The programming languages such as C or C++, used for
developing the embedded program for a microcontroller would be an
advanced and a vast topic. In this chapter, I will not be able to cover all
concepts of the programming language. Nevertheless, nothing should
discourage the readers from picking-up on some of the terminologies or
concepts on this topic if the readers are interested. The idea to be highlighted
again: to start our journey, we will not need to know all the concepts of the
programming language right at the beginning. As many resources are
available online, we could always re-use many of the codes written by
various people for different devices and could just tweak the code a little to
make our gadget. In the following section, some of the useful concepts and
terminologies of the programming languages used for Arduino® are
introduced. If the readers need to refer to the complete Language Reference,
they would need to visit: https://www.arduino.cc/reference/en/.
Sketch: In Arduino® community language, one complete code for
Arduino® is called a “sketch”. This file gets saved in the hard drive with a
“.ino” extension. We could also open this file in Notepad++ (a good editor
for writing or viewing codes, available free of cost). An Arduino® sketch
contains two built-in functions: setup() and loop(). These two are the main
elements of any Arduino® code.
Syntax: The exact style following which the code shall be written is called
syntax. We must follow the syntax; otherwise, the compiler will throw errors.
Please remember that the languages such as C or C++ are case sensitive;
hence we need to use the exact case for the keywords or variables.
Semicolon: It is essential to note that “;” is used to end a statement.
Forgetting to end a line with a semicolon will result in a compiler error.
Constant: Constants are predefined expressions or values in the Arduino®
language. A few of the constants are listed below.
Constant Description
Name
true The equivalent of logic ‘1’.
false The equivalent of logic ‘0’.
HIGH Digital signal level of an input or output pin. When a
pin is configured to OUTPUT with pinMode(), and set
to HIGH with digitalWrite(), the pin is at 5V for
Arduino® UNO.
LOW When a pin is configured to OUTPUT with pinMode()
and set to LOW with digitalWrite(), the pin is at 0
volts.
INPUT A pin can be configured as an input using the
pinMode() function such as:
const int buttonPin = 2;
…
pinMode(buttonPin, INPUT);
INPUT_PULLUP The ATmega microcontroller on the Arduino® has
internal pull-up resistors that we can access. If we use
internal pull-up resistors, we could use the
INPUT_PULLUP argument in pinMode().
OUTPUT A pin can be configured as an output using the
pinMode() function such as:
pinMode(LED_BUILTIN, OUTPUT);
LED_BUILTIN The constant LED_BUILTIN is the number of the pin
to which the on-board LED is connected. The UNO
board has this LED connected to digital pin 13.
char a = ‘A’;
int myArray[4];
int myArray[]={21,32,43,54};
#include <Wire.h>
…
void setup(){
…
}
void loop(){
…
}
If the library file is placed in the same project folder (e.g. for a custom
library), we could also use double quotes (““) in place of (<>) and include the
name of the file in double-quotes. Then, the compiler finds the library file in
the project folder and does not take this from the default search path for the
Arduino® libraries.
#define: This is a directive for the compiler for defining a constant so that
the compiler uses the same during compilation. That constant does not
occupy any space in the memory.
Function: In straightforward terms, a function defines a reusable code-
block, which can be used in the same or the other sketches or functions. We
are going to use functions a lot in the following examples illustrated in the
following chapters. Writing a custom function might be tough for a beginner,
but using the function available in the library would make things easier for
us. Hence let us focus on using the built-in functions instead of trying to write
our function for now. We would get a complete list of available functions in
the official Arduino® webpage: https://www.arduino.cc/reference/en/.
However, let us get ourselves familiar about the syntax of a function and the
syntaxes for calling a function in our code:
As shown above, a function shall have a name. The arguments are the
parameter values that should optionally be passed into the function. The
“Return_type” is the data type of the variable returned by the function. If the
function does not return any value, it should be written as “void”. For
example, for the Arduino® built-in functions, we need to write “void setup()”.
Within the “{ }” the function statements shall be written.
To call a function, when it does not return a value, we just need to write:
int x=12;
int y=4;
int a,s,m,d,r;
a = x + y; //result: a = 16
s = x – y; //result: s = 8
m = x * y; //result: m = 48
d = x / y; //result: d = 3; denominator MUST be a NON ZERO value
r = x % y; //result: r = 0
Comparison operations: These operations compare two variable and
return “true” or “false” if the relation holds or false, respectively. The
examples in the table below assume A = 5, B = 10.
12 Usually the term “flashing” is commonly used to mean “programming the flash memory” in short.
CHAPTER 11
Analog Input
s we have now learned how we could upload the program image into
A the Arduino® and have seen it running on the Arduino® UNO board,
hopefully, my readers might have now gathered courage for venturing
into coding all by themselves. As we have discussed in Chapter 10, there are
two main blocks setup() and loop() where we are going to include our code.
To start with, we could get the empty code template from the menu File
→ Examples → 01.Basics → BareMinimum as it is shown in Figure 61. The
“BareMinimum” code is an Arduino® code template, as shown in Figure 62.
As like in the “Blink” code, the setup() and loop() functions are in this code
template, but those are left blank. We need to add our code there.
In this chapter, we will read an Analog Input. There is a function for
reading an analog input already made available in the Arduino® library. The
function is analogRead(). This function reads the analog voltage value in the
range 0-5V and returns a count value between 0 to 1023. Now, from where
does this number 1023 appear? In Chapter 6, we have seen that the resolution
of the ADC inside ATmega328 microcontroller is 10-bit. That means, the
ADC converts its entire input range into 0 to (210-1) = 1023 counts. For 2.5V,
this function will return 2.5*1023/5 = 512. The argument for this function is
the pin name of the corresponding analog input, which is being read. For
Arduino® UNO, the argument for this function could be any one of A0 to A5.
We need to write a line of code below to read the analog voltage connected to
pin A0:
analogVal = analogRead(A0);
Figure 62: BareMinimum Code Template
The “AnalogReadSerial” code is shown in Figure 64. The code reads the
analog voltage signal connected to the Arduino® analog input A0 every one
millisecond and stores the value in a variable called “sensorValue”. The same
variable is sent to the PC through the serial port of Arduino®, which could be
displayed on the “serial monitor” tool built-in to the Arduino® IDE tool. The
Serial Monitor tool pops up as a separate window, that acts as a serial
interface terminal. This tool is a “User Interface” for receiving and sending
serial data to the connected Arduino® board. The Serial Monitor tool sends or
receives data on the same COM port of the PC, to which the Arduino® UNO
board is connected. The Serial Monitor often serves as a great debugging tool
while debugging an issue.
Coming back to the code shown in Figure 64, the AnalogReadSerial code
uses the functions described in Table 30:
Functions Description
Serial.begin() Sets the data rate in bits per second (baud) for serial data
transmission. In this code, baud is set as 9600 bits/second.
Serial.println() Prints data to the serial port as human-readable ASCII text
followed by a carriage return character (ASCII 13, or ‘\r’)
and a newline character (ASCII 10, or ‘\n’). In this code,
the value contained by the variable sensorValue is written
in a new line on the Serial Monitor console every time this
function is executed.
analogRead() Reads the value from the specified analog pin. In this
code, this function reads the analog voltage on the
Arduino® pin A0.
delay() Pauses the program for the amount of time (in
milliseconds) specified as the parameter. In this code,
after the program reads the value and sends the value
serially to the PC, it pauses for one millisecond before it
does it again.
Table 30: Functions Used in AnalogReadSerial Code
We could test this code by making a simple circuit. For this circuit, we
need a variable resistor or potentiometer.
In Figure 66, the screenshot of the serial monitor readings is captured for
the set-up described. Based on a preset position of the trimpot’s wiper, the
Arduino® UNO board in the set-up reads 370 counts at the start. When the
wiper nob or screw is rotated, the value changes to 230. This count values
could be converted into voltage as (370*5)/1023 = 1.8V or (230*5)/1023 =
1.12V. Using a multimeter, we could measure the voltage of the signal
connected to the A0 pin with reference to the GND.
Let us experiment with another example code that is made available with
the Arduino® IDE installation. We could get this code from File → Examples
→ 01.Basic → ReadAnalogVoltage. In this code an additional line of code is
added to convert the count into voltage:
Figure 67: The Change Of Analog Voltage On A0 Being Reflected On The Serial Monitor
In the snapshot captured in Figure 67, it could be seen that the wiper
screw of the potentiometer is rotated quickly to the two extreme ends and the
maximum and minimum counts 1023 and 0 are read by the Arduino® and
send serially to the serial monitor console.
Now, suppose, we need to read two sensor inputs. You could improvise
very easily. We need to assign two sensor values to two variables, as shown
below or in Figure 68.
We will now try this out on the Arduino® UNO board. For this
experiment, we need one LED and a resistor of value, say 220 ohms. Connect
the resistor and the LED in series and connect the LED be-tween digital
output pin D9 and GND. Note that, we need to connect the LED in the
correct direction such that cathode of the LED is connected to GND, as
shown in Figure 30. If we take a leaded LED (Through-hole type), the
Cathode (-) lead will be smaller in length compared to the anode (-) lead, and
the LED case will be marked with a flat edge on the cathode side as shown in
Figure 70.
Once we are ready with the circuit, let us focus on the code. Here again,
the reference code is available for us to get started. Open File → Examples
→ 03.Analog → Fading as shown in Figure 71. This code “fades” an LED in
and out alternatively using the PWM signal generated by analogWrite()
function.
Next, we need to compile the code and upload the same into the Arduino®
UNO board. Once the upload is successful, we will see the LED connected to
D9 is fading in and out alternatively. Isn’t it fun? We could add more fun to
this project by fading multiple colour LEDs alternatively using multiple
PWM outputs. By reusing the “Fading” code, the code for fading the multiple
LEDs could be written.
Thus far, you might have developed an inquisitiveness for exploring
coding a bit further. Maybe, I could help you with a problem statement,
which you would want to solve. In the previous chapter, you have seen how
you could read an analog input voltage, which could be varied in the range 0-
5V using a potentiometer. In this chapter, we have explored how to use PWM
outputs to control its brightness of LEDs or simply dimming LEDs. Now,
could you control dimming an LED using a potentiometer connected to A0
pin of Arduino®? More specifically, if the wiper nob is turned clockwise, the
brightness of the LED should increase, and if the nob is turned anti-
clockwise, the LED’s brightness should reduce as the nob is turned and
would turn-off. I am sure you can do it13.
13 Hint: Read the potentiometer input as in analog input signal and drive the PWM output
proportional to the same. Take a look at the example: File → Examples → 03.Analog →
AnalogInOutSerial
CHAPTER 13
Writing Code for LCD Display
The connections between the LCD and Arduino® UNO could be made
using a breadboard, jumper wires, one 10K potentiometer and a 220-ohm
resistor. Table 32 shows the list of components we would need for this
project. This list of components would help us in getting the required
components handy before we start our project. This kind of component list is
also referred to as a Bill of Material or BoM in hardware engineering. Once
we have the parts, we could complete the breadboard circuit like it is shown
in Figure 73.
After the breadboard connections are made, it is time to write the code in
the Arduino® IDE. Before trying this out on the real hardware, we could
simulate the same in the “Circuits” tool on tinkercad.com. After we open
Arduino® IDE and create a new sketch by clicking File → New as shown in
Figure 74, the new sketch would pop-up in a new window with a default
name. We could save this with a different name. I chose the name as
“myLCD” and saved the sketch with that name. Here again, things are more
straightforward than my readers might be thinking as the libraries are
available. For the 16x2 LCD with an 8-bit or 4-bit parallel interface, we will
use “LiquidCrystal” library. This library is developed for enabling the
Arduino® boards to interface with the LCDs with Hitachi HD44780 (or a
compatible) chipset. To know more about this library and the built-in
functions, we could visit https://www.arduino.cc/en/Reference/LiquidCrystal.
We would refer to the example codes mentioned on the same page to
understand how these functions could be used in the program for developing
similar features into the application.
#include <LiquidCrystal.h>
This function maps the Arduino® UNO pins D12, D11, D5, D4, D3, D2
to the LCD pins RS, EN, DB4, DB5, DB6, DB7 respectively. These
assignments are at par with what is described in Table 31 before. This
function also creates a variable of type “LiquidCrystal” with the name “lcd”.
In other words: “lcd” is a variable, which could contain datatype
“LiquidCrystal”. Please note that this is done outside setup() and loop()
functions. As the functions for driving the LCD are defined inside the
“LiquidCrystal” library, we need to call these functions by adding “lcd.”
before the function name. The functions for initializing the LCD registers and
displaying a message are written inside the setup() function.
We have already have seen that the code written inside setup() is executed
only once. Since we are going to display only one message, it should be okay
to put this code in setup(), and we need not write anything inside loop(). That
is why we are not seeing any code written inside the loop() function in this
example, as shown in Figure 75. Nevertheless, we need to remember to keep
the loop() {} function in our code, even if it is empty to avoid compiler error.
The first function in setup(), lcd.begin(16,2) initializes the LCD display
registers. The next function lcd.setCursor(0,0) sets the cursor at the first
character of the first line on the display. In the next line in the code, the
function lcd.print() prints the text mentioned within the double-quotes. The
following function lcd.setCursor(0,1) shifts the cursor to the first character of
the second line on the display. Lastly, the function lcd.print() prints the text
“Arduino®” in the second line. We could try uploading this code into the
hardware by simulating on tinkercad.com, or we could try this on an
Arduino® UNO hardware if the wiring is made. We will see this message
displayed like it is shown in Figure 73.
Figure 75: Simple Code For Displaying a Text On A 16x2 LCD Display (Parallel Interface)
We could also write a text on the second line and scroll both the lines
together by using the same function lcd.scrollDisplayLeft() as shown in
Figure 78. Once we run this code, both the text messages displayed on both
lines will get scrolled together. Isn’t that easy and fun?
If we want to keep the wiring to the display more straightforward, we
could buy one LCD with the I2C interface. In Figure 79, the Grove LCD with
I2C interface is being driven from an Arduino® UNO compatible Lotus board
from Seeed Technology Co. Ltd. The advantage here is that wiring to the
display becomes much convenient and we have the pluggable connector
wires available. As this Grove display has the I2C interface and this is not the
same as the LCD with the 8-bit parallel display, we need to use a different
library in our code to drive the display from Arduino® UNO or equivalent
board. In Figure 79, I have used the Grove library “rgb_lcd” downloaded
using the Library Manager tool on the Arduino® IDE. We would need to
launch the Library Manager tool by clicking on the Tool → Manage Libraries
menu on the toolbar or by pressing the short key “ctrl + shift + I”, type
“Grove LCD” in the search window. The “Grove – LCD RGB Backlight”
library will appear in the Library Manager window. If it is already installed in
the PC, it will be shown as “installed” (refer Figure 80). If it is not already
installed, we will get the “Install” button for installing the same.
Figure 78: Scrolling Both Lines In The Display
After we install the Grove LCD library, it will appear in the File →
Examples menu as “Grove – LCD RGB Backlight” as shown in Figure 81.
We could then connect the Grove RGB LCD with I2C interface to the
Arduino® UNO or compatible board. We should be able to try out any of the
sketches available in this Grove library. In my set-up shown in Figure 79, I
have slightly modified the “HelloWorld” code to display the message, which
I wanted to get displayed on the LCD. The code used is shown in Figure 82.
Please note that there are two libraries use: Wire and rgb_lcd in this code.
The library “Wire” is used for configuring and working with the I2C interface
on Arduino® UNO. The other library rgb_lcd is similar to the LiquidCrystal
library but meant to work with the “Grove – LCD RGB Backlight” display.
After including the libraries, the following statement: rgb_lcd lcd creates a
variable type “rgb_lcd” assigned with a name “lcd”, very similar to what we
have seen in the parallel display example. As we have seen before, all
function built into the rgb_lcd will now be called with an “lcd.” preceding the
respective function names. In the next three lines of the code, three constants
of “integer” type – colorR, colorG, colorB are declared. These are passed as
parameters later in the function lcd.setRGB() to turn the LCD backlight on.
Thus far, we should already be familiar with the rest of the functions, which
were discussed earlier.
Figure 79: Interfacing Grove 16x2 LCD With Arduino® UNO Compatible Seeed Studio Lotus Board
Using I2C Interface
Figure 83: Temperature And Humidity Indicator Using Arduino Compatible Board
Figure 85: Launching The DHTtester Code From The Grove Temperature & Humidity Library
The default configuration of this code is meant to work with the DHT22
sensor. The “#define” statement defining “DHTTYPE” as “DHT22” does the
same. The #define statement is a useful C/C++ component that allows the
programmer to assign a name to a constant value before the program is
compiled, which saves on Arduino® memory. As I was using a DHT11
sensor, I needed to make a slight change in this section by commenting out
the #define line for the DHT22 sensor and un-commented the same for the
DHT11 sensor as shown in Figure 87.
With this change, I wanted to try this code on my set-up shown in Figure
83. So, I compiled this code and uploaded the same to the Seeed Studio Lotus
board.
Figure 87: The Code Change Done To Configure The Code To Work with DHT11 Sensor
After the upload was successful, I opened the serial monitor by clicking
on the Tool → Serial Monitor menu. Please note that the Serial Monitor tool
could also be launched by pressing “Ctrl+Shift+M”. Once the Serial Monitor
was open, the temperature and humidity data started getting logged, as shown
in Figure 88. If we do not see any data on the serial monitor tool while trying
this code, we need to ensure the serial data transfer rate (Baud Rate) setting is
correct as 115200 bits/sec as shown in Figure 88. The baud rate configured
on the serial monitor must match the same that is set in the code. We could
find this in the setup() function: the very first line has this function called:
SERIAL.begin(115200);
This line of code configures the serial port data rate as 115200 baud or
bits/second. Hence, we need to set the same value on the Serial Monitor too.
Figure 88: Temperature And Humidity Log On The Serial Monitor Tool
Next, I needed to merge the Grove RGB LCD code with the DHTtester
sketch to display the temperature and humidity data on the Grove LCD
instead of the Serial Monitor tool. First, I renamed the sketch as
“DTH11_LCD”. Then, I included the required library “rgb_lcd.h” for driving
the Grove LCD followed by copying the portion of the code which was
written outside both the setup() and loop() functions. The added lines of
codes are shown in boxes in Figure 89.
As we have seen in the previous chapter, the statement: “rgb_lcd lcd”
creates a variable type rgb_lcd with the name “lcd”. Similarly, the statement:
“DHT dht (DHTPIN, DHTTYPE)” creates a variable type DHT having name
“dht” and initializes it with two constants “DHTPIN” (=2) and DHTTYPE (=
DHT11) so that the functions in the class “DHT” would work correctly with
the DHT11 sensor connected to the pin# D2 of Arduino® UNO. This might
sound complex but trust that we need not know more profound into the
programming language as of yet, for starting with our Arduino® sketches. We
just need to know where to tinker and improvise. That should automatically
help us in our learning journey.
Figure 89: Merging LCD Code With The DHTtester Sketch – Outside SETUP () and LOOP ()
functions
Next step for me was to merge the codes written inside the setup()
function. Again, I copied the portion of the code written inside setup()
function, from my previous LCD display project (refer Chapter 13) and
pasted the same inside the setup() function of the DTH11_LCD sketch. The
changes and inclusions are shown in boxes in Figure 90. The first two lines
were commented out as those were meant to use the serial port driver
functions, and I did not need it anymore. Next, the code to initialize the LCD
and to turn the backlight on was included as shown. The remaining lines of
code already existed in the DHTtester sketch, and I kept those unchanged.
hence the cursor was set to the first character of the first line by executing
the statement: “lcd.setCursor(0,0);”. The next line of code prints the string
“RH:” starting from the first character of the first line. In the lcd.print()
statement, anything mentioned within double quotes is considered as a
character or a string (collection of characters). The content mentioned within
the double quote is displayed as specified. When the value of a variable is to
be printed, double quotes are not used. Instead, the name of the variable is
mentioned inside the first brackets of the lcd.print() function call. The
statement: “lcd.print(temp_hum_val[0])” does the same. It prints the value of
the float variable temp_hum_val[0], which stores the humidity data read from
the DHT11 sensor. The following statement prints the percentage symbol
“%” as a text. The next set of statements are similar to the statements for
printing the humidity number. These following statements print a text
“Temp:” and then prints the value of the variable temp_hum_val[1] followed
by a string “*C” to indicate degree centigrade.
In the else statement block, the failure condition is included, and that is
executed in case Arduino® fails to communicate with the sensor. It displays a
message that “Failed to get temperature and humidity value”. As the length of
this message is more than 16 characters, the last few characters in this string
will not be displayed. Hence, we could either shorten it or could use
lcd.scrollDisplayLeft() function to scroll the message to display the entire
message.
Outside the “if-else” statement, there are two more statements in the
loop() function block. The delay() function allows a delay between each
reading. In my program, it is 4 seconds, which could be increased further as
the room temperature and humidity do not change significantly that fast. The
lcd.clear() function clears the current data displayed. As all these statements
are executed in the loop() function, the program reads the recent data from
the sensor, display the same on the LCD screen and waits for the delay
repeatedly. After I uploaded this program in my Lotus board, I started getting
the readings on the LCD, as shown in Figure 83.
As it can be seen, it is not that complex to build a complete project using
Arduino® UNO or compatible boards, even if we might not know the
programming language such as C/C++ in depth. As there are so many
resources available online and we would most probably find the library for
the input or output device that we would need to connect with Arduino®
UNO for our project, things are much more straightforward. We need to start
experimenting and indeed, we will pick-up on the language. If we want to
learn more about the syntax and programming languages, we will get many
resources14 online apart from the tutorial provided on the official Arduino®
website: www.arduino.cc. Start building and explore online resources to
continue your learning journey. Wish you all the very best!!