Embedded System Project 6: ESP32 Serial Communication

Michelle Lim
7 min readMar 19, 2023

--

Hi, I’m Michelle and today I’ll be continuing my blog series of the embedded systems project! ✨🤩 (You can read the previous blog here 🤗)

The sixth project I’ll be doing is about serial communication on ESP32. Serial communication is the most widely used approach to transfer information between data processing peripherals. Every electronic device, both PC and mobile, runs on serial communication. The protocol is the secure and reliable form of communication having a set of rules addressed by the source host, the sender, and the destination host, the receiver.

Serial communication protocols can be divided into two, the synchronous serial protocols, such as SPI, I2C, CAN, and LIN, and the asynchronous serial protocols, such as UART.

Serial Communication Protocol

Since today’s project will be using the ESP32 development board, BME280 sensor, and I2C 16x2 LCD which communicate via the I2C communication protocol, I will explain more about the I2C communication protocol. 😄

I2C or Inter-Integrated Circuit is a communication protocol with half-duplex transmission method. In this method, both sender and receiver can be active but not at the same time. If the sender is transmitting, the receiver can accept but not send and similarly vice versa. One of the well-known examples of half-duplex is the internet where the user sends a request for data and gets it from the server. Additionally, I2C is also one of the synchronous serial protocols as mentioned before. In synchronous serial interface, a point-to-point connection from a master to slave, all devices use single CPU bus to share data and clock. Besides that, I2C is used for serial data transmission for short distance between boards, modules, and peripherals which uses 2 pins.

Furthermore, the I2C is a two-line communication between different ICs or modules where two lines are SDA (Serial Data Line) and SCL (Serial Clock Line) as shown on the picture above. Both the lines must be connected to a positive supply using a pull up resistor. I2C can deliver speed up to 400Kbps and uses 10 bit or 7 bit addressing system to target a specific device on the I2C bus so that it can connect up to 1024 devices. As it has limited length communication, it is ideal for onboard communication purposes. I2C networks are easy to setup since it uses only two wired and new devices can simply be connected to the two common I2C bus lines. An advantage of I2C compared to SPI and UART is that it is the only protocol that ensures the data was received by the device. However, this can lead to a “locked” up state by one device under adverse conditions.

The ESP32 has several I/O interfaces, one of them is the two I2C interfaces. These two bus interfaces can serve as I2C master of slave. With multi-master, multi-slave communication protocol, the communication can be multiple slaves to one master or multiple masters controlling the same slave. Example of the multiple slaves to one master is when the ESP32 reads from a BME280 sensor using I2C and writes the sensor readings in an I2C OLED display. While the example of the multiple masters controlling the same slave is when two ESP32 boards writing data to the same I2C OLED display.

ESP32 Bus Interface

Project: Introduction

The project we’ll be doing today is connecting the ESP32 to I2C LCD and BME280 to get the ESP32 to write BME280 measurements to the I2C LCD.

The components we’ll be using in this project are listed below.

  1. ESP32 Development Board
  2. Micro-USB Cable
  3. Breadboard
  4. 16x2 I2C Liquid Crystal Display (LCD)
  5. BME280 Sensor
  6. Male-to-Female, Male-to-Male, Female-to-Female jumper wires
  7. Laptop/PC with Arduino IDE installed and set

Project: Getting I2C Address

1. Wire each of the devices to ESP32, copy the code block below, compile, and upload it on your Arduino IDE to get I2C address of each of the BME280 sensor and I2C LCD. (note: this is the same code as the one I posted on my Project 5 blog)

Wiring: ESP32-LCD , ESP32-BME280

/*********
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);
}

2. After uploading the code, open the serial monitor at baud rate of 115200. Press the ESP32 EN button and the I2C address should be displayed in the serial monitor.

I2C address of LCD
I2C address of BME280

Project: Schematics — Connecting ESP32, I2C LCD, BME280 Sensor

Follow the schematics below 😄

Schematics to connect ESP32 — I2C LCD — BME280 Sensor
My arrangements for reference

Project: Display Static Text of BME280 Readings on I2C LCD

Copy the code below! 😄

//bme280
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <LiquidCrystal_I2C.h>
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C
unsigned long delayTime;

//lcd
#include <LiquidCrystal_I2C.h>
// set the LCD number of columns and rows
int lcdColumns = 16;
int lcdRows = 2;
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);


void setup() {
Serial.begin(9600);
Serial.println(F("BME280 test"));

bool status;

// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin(0x76);

if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}

Serial.println("-- Default Test --");
delayTime = 1000;

Serial.println();

// initialize LCD
lcd.init();
// turn on LCD backlight
lcd.backlight();

}

void loop() {
float temp = bme.readTemperature();
float pres = bme.readPressure() / 100.0F;
float alt = bme.readAltitude(SEALEVELPRESSURE_HPA);
float humid = bme.readHumidity();

String messageToScroll, messageToScroll2;
lcd.setCursor(0, 0);
lcd.clear();

if(isnan(temp)||isnan(pres)||isnan(alt)){
lcd.print("Failed to read.");
}
lcd.clear();

lcd.setCursor(0, 0);
lcd.print("Temp: ");
lcd.print(temp);
lcd.print(" C");
lcd.setCursor(0, 1);
lcd.print("Pres: ");
lcd.print(pres);
lcd.print(" hPa");

delay(2000);
lcd.clear();

lcd.setCursor(0, 0);
lcd.print("Alt: ");
lcd.print(alt);
lcd.print(" m");
lcd.setCursor(0, 1);
lcd.print("Humid: ");
lcd.print(humid);
lcd.print(" %");

delay(2000);
lcd.clear();
}

This code combines the codes from displaying static text in Project 5 and BME280 readings in Project 4, with few modifications.

Compile and upload, and this is what you should get! 😉

BME280 Readings displayed as Static Text on I2C LCD

Project: Display Scrolling & Static Text of BME280 Readings on I2C LCD

Copy the code below! 😄

//bme280
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include <LiquidCrystal_I2C.h>
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C
unsigned long delayTime;

//lcd
#include <LiquidCrystal_I2C.h>
// set the LCD number of columns and rows
int lcdColumns = 16;
int lcdRows = 2;
LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);


void setup() {
Serial.begin(9600);
Serial.println(F("BME280 test"));

bool status;

// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin(0x76);

if (!status) {
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1);
}

Serial.println("-- Default Test --");
delayTime = 1000;

Serial.println();

// initialize LCD
lcd.init();
// turn on LCD backlight
lcd.backlight();

}

void scrollText(String message, String message2, int delayTime, int lcdColumns) {
for (int i=0; i < lcdColumns; i++) {
message = " " + message;
message2 = " " + message2;
}
message = message + " ";
message2 = message2 + " ";
for (int pos = 0; pos < message.length(); pos++) {
lcd.setCursor(0, 0);
lcd.print(message.substring(pos, pos + lcdColumns));
lcd.setCursor(0, 1);
lcd.print(message2.substring(pos, pos + lcdColumns));
delay(delayTime);
}
}

void loop() {
float temp = bme.readTemperature();
float pres = bme.readPressure() / 100.0F;
float alt = bme.readAltitude(SEALEVELPRESSURE_HPA);
float humid = bme.readHumidity();

String messageToScroll, messageToScroll2;
lcd.setCursor(0, 0);
lcd.clear();

if(isnan(temp)||isnan(pres)||isnan(alt)){
lcd.print("Failed to read.");
}
lcd.clear();

messageToScroll = "Temperature: " + String(temp) + " C";
messageToScroll2 = "Altitude: " + String(alt) + " m";
scrollText(messageToScroll, messageToScroll2, 150, lcdColumns);
delay(2000);

lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Pres: ");
lcd.print(pres);
lcd.print(" hPa");
lcd.setCursor(0, 1);
lcd.print("Humid: ");
lcd.print(humid);
lcd.print(" %");

delay(2000);
}

This code simply combines the codes from displaying scrolling text and static text in Project 5 and BME280 readings in Project 4, with few modifications.

Compile and upload, and this is what you should get! 😉

BME280 Readings displayed as Scrolling and Static Text on I2C LCD

So yea, that’s the end of our sixth Embedded System Project: ESP32 Serial Communication. (yeah!! 🥳) Stay tune for the next projects and stay safe and healthy 🥰

--

--