Intro to Embedded System with ESP32 [Output — Display & PWM]

Reinhart Lim
6 min readFeb 25, 2023

--

In this project, I’ll share my experience with Liquid Crystal Display aka LCD and PWM control. The LCD I’ll use will be a 16 x 2 I2C LCD.

— Component Required

  • LCD 16 x 2
  • ESP32
  • Jumper wires

When you buy the LCD make sure it has I2C and soldered it to the LCD.

What is I2C?

I2C stands for Inter-Integrated Circuit. It is a bus interface connection protocol incorporated into devices for serial communication.
-GeeksforGeeks
https://www.geeksforgeeks.org/i2c-communication-protocol/

— LCD Display

Here’s a simple schematic we’ll follow to experiment with the LCD:

https://randomnerdtutorials.com/esp32-esp8266-i2c-lcd-arduino-ide/
Pins reference

Before we begin the project, we’ll need to download the LCD library from the library manager, as described in the last blog.

— Getting LCD Address

/*********
Rui Santos
Complete project details at https://randomnerdtutorials.com
*********/

#include <Wire.h>

void setup() {
Wire.begin();
Serial.begin(115200);
Serial.println("\nI2C Scanner");
}

void loop() {
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ ) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
nDevices++;
}
else if (error==4) {
Serial.print("Unknow error at address 0x");
if (address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
}
}
if (nDevices == 0) {
Serial.println("No I2C devices found\n");
}
else {
Serial.println("done\n");
}
delay(5000);
}

First, run this code to get the I2C address for further usage. Here’s the address result:

LCD address

— LCD Code

/*********
Rui Santos
Complete project details at https://randomnerdtutorials.com
*********/

#include <LiquidCrystal_I2C.h>

// set the LCD number of columns and rows
int lcdColumns = 16;
int lcdRows = 2;

// set LCD address, number of columns and rows
// if you don't know your display address, run an I2C scanner sketch
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);

void setup(){
// initialize LCD
lcd.init();
// turn on LCD backlight
lcd.backlight();
}

void loop(){
// set cursor to first column, first row
lcd.setCursor(0, 0);
// print message
lcd.print("Hello, World!");
delay(1000);
// clears the display to print new message
lcd.clear();
// set cursor to first column, second row
lcd.setCursor(0,1);
lcd.print("Hello, World!");
delay(1000);
lcd.clear();
}

The address we’ve got before was used in

LiquidCrystal_I2C lcd(lcdAddress, lcdColumns, lcdRows)

We fill the address with 0x27 that we’ve got from the address-getter code. The LCD column and rows were 16, 2 because we use LCD with that size.

lcd.setCursor(column,row) function will set the position of the displayed value.

lcd.print() function was used to print the value to the LCD.

Here’s the result after running the code:

Result

— Scrolling Text

#include <LiquidCrystal_I2C.h>

// set the LCD number of columns and rows
int lcdColumns = 16;
int lcdRows = 2;

// set LCD address, number of columns and rows
// if you don't know your display address, run an I2C scanner sketch
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);

String messageStatic = "Static message";
String messageToScroll = "This is a scrolling message with more than 16 characters";

// Function to scroll text
// The function acepts the following arguments:
// row: row number where the text will be displayed
// message: message to scroll
// delayTime: delay between each character shifting
// lcdColumns: number of columns of your LCD
void scrollText(int row, String message, int delayTime, int lcdColumns) {
for (int i=0; i < lcdColumns; i++) {
message = " " + message;
}
message = message + " ";
for (int pos = 0; pos < message.length(); pos++) {
lcd.setCursor(0, row);
lcd.print(message.substring(pos, pos + lcdColumns));
delay(delayTime);
}
}

void setup(){
// initialize LCD
lcd.init();
// turn on LCD backlight
lcd.backlight();
}

void loop(){
// set cursor to first column, first row
lcd.setCursor(0, 0);
// print static message
lcd.print(messageStatic);
// print scrolling message
scrollText(1, messageToScroll, 250, lcdColumns);
}

You can custom the static and scrolled message like this:

Scrolling Text

— Current DateTime

I did some modifications to the SimpleTime code from Arduino IDE and display the current DateTime to the LCD (GMT+7).

#include <WiFi.h>
#include "time.h"
#include <LiquidCrystal_I2C.h>

// set the LCD number of columns and rows
int lcdColumns = 16;
int lcdRows = 2;
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);

const char* ssid = "your_wifi";
const char* password = "wifi_password";

const char* ntpServer = "asia.pool.ntp.org";
const long gmtOffset_sec = 25200;
const int daylightOffset_sec = 60000;

void printLocalTime()
{
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
Serial.println("Failed to obtain time");
return;
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");

lcd.setCursor(0,0);
lcd.print(&timeinfo,"%b %d %Y");
lcd.setCursor(0,1);
lcd.print(&timeinfo,"%H:%M:%S");
}

void setup()
{
Serial.begin(115200);

//connect to WiFi
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" CONNECTED");

//init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
lcd.init();
lcd.backlight();
printLocalTime();

//disconnect WiFi as it's no longer needed
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}

void loop()
{
delay(1000);
printLocalTime();
}

Change the value according to your wifi.

const char* ssid       = "your_wifi";
const char* password = "wifi_password";

After running the code your LCD will show the current date and time(GMT+7) like this:

Current DateTime

— PWM

Now, let’s continue to PWM signaling with ESP32. In this project, we’ll build a simple project by dimming LED lights using an LED PWM controller.

Here’s a simple schematic we’ll follow to experiment with the PWM:

ESP32 PWM with Arduino IDE (Analog Output) | Random Nerd Tutorials

— PWM Code

// the number of the LED pin
const int ledPin = 16; // 16 corresponds to GPIO16
const int ledPin2 = 17; // 17 corresponds to GPIO17
const int ledPin3 = 5; // 5 corresponds to GPIO5

// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;

void setup(){
// configure LED PWM functionalitites
ledcSetup(ledChannel, freq, resolution);

// attach the channel to the GPIO to be controlled
ledcAttachPin(ledPin, ledChannel);
ledcAttachPin(ledPin2, ledChannel);
ledcAttachPin(ledPin3, ledChannel);
}

void loop(){
// increase the LED brightness
for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){
// changing the LED brightness with PWM
ledcWrite(ledChannel, dutyCycle);
delay(15);
}

// decrease the LED brightness
for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
// changing the LED brightness with PWM
ledcWrite(ledChannel, dutyCycle);
delay(15);
}
}

ledcSetup(ledChannel, freq, resolution)function accepts arguments: the ledChannel, the frequency, and the resolution.

ledcAttachPin()function accepts arguments: GPIO and the channel that’s generating the signal. After that in the for loop function, using ledcWrite from 0 to 255 and vice versa, we’ll change the LED brightness up and down.

Here’s the result when we run the code on the schematic above:

PWM experiment result

We can see that the LED light begins to dim and then bright again.

By following the step above you’ve already finished this simple project about ESP32 I2C LCD. Congrats 🎉. This is simply a simple experiment with LCD; you may try more complex circuits and programs.

This blog can be helpful for those who are learning and getting started using ESP32. That’s all for this project, stay tuned for the next experiment with ESP32 !!

--

--