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:
-
Setting the pin mode of an Arduino digital pin.
-
Sending and receiving values to and from an Arduino digital pin.
-
Sending and receiving values to and from an Arduino digital pin.
-
Structures
, unions
, and how to use them.
-
Adding tabs to the IDE Client/Web Editor
-
Classes
, objects
, and how to use those structures.
Setting Digital Pin Mode
In previous sections, we learned that an Arduino micro-controller board has a set of digital pins, that receive or send digital data (i.e., zeros and ones). These can only work when they’re assigned the ability to either send or receive data. This is done by using the ‘pinMode()’ function. For basic examples like ours and smaller sketches, this function is called during ‘setup()’ execution, though do keep in mind that this is a function that can be called anywhere you
wish.
The syntax for this function defines two parameters: ‘pin’ is the number of the pin being set and ‘mode’ being the setting of this pin, which can be given the argument ‘INPUT’ or ‘OUTPUT’ to read or send values from that pin.
pinMode(pin, mode);
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);
}
|
This code assigns ‘#define’constants BUTTON_ONE and LED_ONE to digital pins 12 and 11, respectively, and then sets the pins so that BUTTON_ONE receives an input and LED_ONE produces an output.
We can also use ‘pinMode()’ to change the pin’s pull-up resistor's functionality by passing the argument ‘INPUT_PULLUP’ to the mode
parameter. This inverts whatever input it receives.
Let’s look at how to use these pins and their settings:
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");
}
}
|
This code sets a ‘#define’ constant BUTTON_ONE to 12, which is used to point out the pin that is set to receive inputs via ‘pinMode().’ The loop reads the current value being sent to that pin via the button’s state and tells it to print out the respective statements for the respective conditions onto the Serial Monitor.
So far we’ve seen how to use digital pins to communicate digital data. This can easily prove to limit, so we can also use analog pins to communicate analog-form, continuous data. These pins work using Pulse Width Modulation (PWM), and the pins for these, for most Arduino boards, are pins 3, 5, 6, 9, 10, and 11. Let’s look at the functions for these analogous (no pun intended) to the digital read and write functions:
Analog Write
The function ‘analogWrite()’ allows us to write an analog value that is sent to a pin using Pulse Width Modulation, which, in short, produces a percentage of the maximum value that is given at digital 1. The syntax for this function is:
‘pin’ specifies what pin the analog value should be written on, and ‘value’ tells it the value that it is supposed to write. This value ranges from 0 to 255 (dividing the maximum value into 255 steps between the low and high states)
The following code describes how we’d use analogWrite() to make an LED glow gradually from being off to being brightly lit:
#define LED_ONE 11
int val = 0;
int change = 5;
void setup()
{
pinMode(LED_ONE, OUTPUT);
}
void loop()
{
val += change;
if (val > 250 || val < 5) {
change *= -1;
}
analogWrite(LED_ONE, val);
delay(100);
}
|
The ‘#define’ constant specifies the number 11, which is used to identify what pin is to be written on (and whether it’s to be written on) in ‘pinMode().’ Two variables are also declared globally, which are ‘val’ and ‘change.’ This stores the current value being sent to the pin and whether it should be changed to increment in the opposite direction according to the conditional in the sketch’s loop. A short delay is added before the loop is to repeat.
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:
Let’s look at an example of this being used to determine the temperature reported by a TMP36 temperature sensor:
#define TEMP_PIN 5
void setup() {
Serial.begin(9600);
}
void loop() {
int pinValue = analogRead(TEMP_PIN);
double voltage = pinValue * 0.0049;
double tempC = (voltage - .5) * 100.0;
double tempF = (tempC * 1.8) + 32;
Serial.print(tempC);
Serial.print(" - ");
Serial.println(tempF);
delay(2000);
}
|
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;
};
|
Creating and using a structure object is simple, though it is a step up from defining integer values to variables. We need to use the ‘struct’ keyword and the structure's name as the datatype for initializing the value. The syntax for our particular example is as follows:
struct tmp36_reading temp;
|
Assigning or reading values stored in the structure’s list of variables is done as follows:
temp.voltage = pinValue * 0.0049;
temp.tempC = (temp.voltage - .5) * 100.0;
temp.tempF = (temp.tempC * 1.8) + 32;
|
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];
};
|
Now, we’ll have a look at how its unique functionality works:
union some_data {
int i;
double d;
char s[20];
};
void setup() {
Serial.begin(9600);
union some_data myData;
myData.i = 42;
myData.d = 3.14;
strcpy( myData.s, "Arduino");
Serial.println(myData.s);
Serial.println(myData.d);
Serial.println(myData.i);
}
|
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.
Working with Tabs
When we make a new tab to work with (or want to do so), the first decision is to figure out what to do with it. Let’s continue our example code and create new tabs - ‘led’ to store functionality and ‘led.h’ to store constants.
Put the following block of code into the ‘led.h’ tab:
#ifndef LED_H
#define LED_H
#define LED_ONE 3
#define LED_TWO 11
#endif
|
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.
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);
|
Use the class method to blink the LED (syntaxed as ‘Led.blinkLed()’ ) in the main tab as follows:
#include "led.h"
Led led(LED_ONE, 1000, 500);
void setup() {
}
void loop() {
led.blinkLed();
}
|
Uploading this code onto our previous prototype will make our LED blink.
Let’s now discuss strings, objects made of character arrays, and Arduino’s libraries for using them:
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); \\ returns a string ‘42’
String strNum2 = String(42, HEX); \\ returns a string ‘2a’
String strNum3 = String(42, BIN); \\ returns a string ‘101010’
|
Other functionalities in the string library include:
-
concat(string), concatenates one string to the prior string’s end
-
endsWith(string): A Boolean conditional that returns true if the first string's end is the same as the argument string.
-
equals(): returns true if both strings are the same.
-
equalsIgnoreCase(): returns true if both strings are the same,
ignoring lower- or upper-case differences
-
length(): provides an integer value of the number of characters inside the string (without the null character).
-
replace(substring1, substring2): replaces all instances of the first argument with the second argument.
-
startsWith(string): A Boolean conditional that returns true if the first string's beginning is the same as the argument string.
-
toUpperCase(): converts a string fully to uppercase, where possible.
-
toLowerCase(): converts a string fully to lowercase, where possible.
Strings can be used in place of character arrays when needed, but be mindful of the fact that it requires more memory and time to execute. More often than not, this constraint is the only reason why character arrays are used for storing text instead of strings.
Arduino Motion Sensor Project
We shall learn, in this section, the process, and techniques for integrating a motion sensor with Arduino hardware. To do so, we shall use a pre-built motion sensor named HC-SR501. Owing to its simplicity of use, reasonable price range, and practical, flexible purpose, it is often one of the first tools that people use with Arduino (and the program) to test and develop an understanding of micro-controllers. It is often included in Arduino starter packages. We will learn how to:
-
Attach the sensor to the Arduino.
-
Read the output of the sensor.
-
Create and read a Fritzing diagram of a completed build.
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.
-
Sensitivity:
Changes the range at which the sensor is effective, from 3 meters to 7 meters. This value goes down with the clockwise rotation of the screw.
-
Output Timing:
Changes the delay or period wherein the sensor puts out a Boolean true state (a ‘high’ or ‘1’ state), from 5 seconds to 300 seconds (5 minutes). This value goes up from the lower end with the clockwise rotation of the
screw.
-
Ground:
Connect to the Ground of your connection (breadboard ground rail, Arduino Ground pin, source ground, etc.)
-
5V:
Connect to the Live of your power source, preferably a 5V voltage (breadboard power rail, Arduino 5V pin, source power, etc.)
-
Output:
Connect this to the relevant Arduino pin of your choosing. This sends out the IC output to the other ends of the wire (i.e., the micro-controller) for the period specified via the Output Timing screw.
Let’s continue onwards and build a circuit using this sensor.
Components
For our circuit we shall require the components listed:
-
Arduino Uno (or similar)
-
HC-SR501 Motion Sensor
-
Jumper Wires
-
Optional:
LED
-
Optional:
Breadboard
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);
}
|
We use #define to create a MOTION_SENSOR variable that will read the state of pin 3 (set via pinmode()). We’ll also set up the serial console via setup().
The main loop (i.e., loop() ) operates as follows: digitalRead() is called to read the sensor output on pin 3, and sensorValue takes up this value. When it is HIGH, a message (‘Motion Detected’) is printed in the serial console. When it is LOW, nothing happens. A 0.5 second delay later, the loop restarts.
Running the Project
Have the console open while the code runs to see the output, and wave something in front of the sensor which would then make the Serial Console print out the message.
Challenge
Try adding an LED that lights up when the sensor goes HIGH. Remember to add a resistor to the circuit. The following code lights
up an LED connected to pin 5 when the condition is met:
#define MOTION_SENSOR 3
#define LED 5
void setup() {
pinMode(MOTION_SENSOR, INPUT);
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
Serial.begin(9600);
}
void loop() {
int sensorValue = digitalRead(MOTION_SENSOR);
if (sensorValue == HIGH) {
Serial.println("Motion Detected");
}
digitalWrite(LED, sensorValue);
delay(500);
}
|
Arduino LCD Display Project
Components required
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:
The Above mentioned libraries are installed because we need them and the SPI library. To do this, the below-written include statements are added at the start of the sketch:
#include <SPI.h>
#include < Adafruit_GFX.h>
#include < Adafruit_PCD8544.h>
We won’t wish to begin an instance of the type i.e., the Adafruit_PCD8544 by utilizing the code written below:
Adafruit_PCD8544 display = Adafruit_PCD8544(13, 11, 5, 4, 3);
The CLK, DIN, DC, CE, and RST pins respectively are connected to the Arduino pin numbers that are basically the parameters.
To set up the Adafruit_PCD8544 instance, we should add the below-written code in the setup ( ) function:
Serial.begin(9600);
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 Larger Text
display.setTextSize(2);
display.setTextColor(BLACK);
display.print("This is larger text");
display.display();
To set the size of the text, the function setTextSize ( ) is used. We can see in the first example that the size of the text is set to ‘1.’ The color of the text is set by using the setTextColor ( ) function. As we know that the display of Nokia 5110 LCD has a monochromatic so only two
colors i.e., Black and White are available. To write the text on the screen, the position of the cursor is set to the position on the screen by using the setCursor ( ) function. For this example, the position of the cursor is set to the upper left corner of the screen. At last, the message i.e., Hello World! is displayed on the screen by using the function println ( ).
In the next example of the above code, the foreground color is set to WHITE and the background color is set to BLACK to reverse the text by using the setTextColor ( ) function. Afterward, to display the value of PI on the screen the println ( ) function is used. As the setTextSize ( ) function is not called so the text size stays the same i.e., ‘1.’
In the third example of the code above, the text's size is set to ‘2’ and the color is set to ‘BLACK.’
Rotating Text
The text can also be rotated. This can be done by the following code:
display.setRotation(1);
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.