IoTSimulator

Display Temperature on an LCD with ESP32-C3

Read ambient room temperature with an NTC thermistor and display live Celsius and Fahrenheit readings on a 16x2 LCD screen using an ESP32-C3 microcontroller.
Muhammad Ichsanul Fadhil
IoTSim Editor
September 27, 2026
Display Temperature on an LCD with ESP32-C3

Live project track

Interactive hardware & logic preview

A digital thermometer is one of the most practical instruments you can build. Whether monitoring a server room, a home greenhouse, or a 3D printer enclosure, having a clear live readout of temperature gives you immediate visibility into your environment. Instead of relying on a pre-packaged display, building your own teaches you how analog sensors translate physical heat into digital numbers.

In this project, you will build a digital weather station using an ESP32-C3 microcontroller, an NTC thermistor temperature sensor, and a 16x2 character LCD. The main idea is simple: the sensor changes its electrical resistance as the room warms up or cools down, the ESP32-C3's 12-bit analog converter reads that change, and the display shows live readings in both Celsius and Fahrenheit.

How the NTC Sensor Reads Heat

An NTC thermistor is a temperature-sensitive resistor whose name stands for Negative Temperature Coefficient. This means that as temperature increases, its electrical resistance drops. When wired with a fixed balancing resistor into a voltage divider circuit, changes in room temperature produce corresponding changes in output voltage.

NTC thermistor reading flow diagram from heat to temperature
NTC thermistor reading flow diagram from heat to temperature

The diagram above illustrates how the measurement travels through the system. Ambient heat lowers the sensor resistance, which raises the voltage entering GPIO 0. The ESP32-C3's 12-bit analog-to-digital converter turns this voltage into an integer between 0 and 4095. Finally, the Steinhart-Hart equation translates that raw number into exact degrees.

Room conditionThermistor resistanceADC reading (0–4095)Calculated temperature
Cold (Refrigerated)High (~30 kΩ)~1100~5.0 °C (41.0 °F)
Room temperatureMedium (~10 kΩ)~2048~25.0 °C (77.0 °F)
Warm / HotLow (~3 kΩ)~3200~50.0 °C (122.0 °F)

Because thermistor resistance follows a gentle curve rather than a straight line, our sketch uses the standard Beta equation to calculate the temperature accurately:

$$\frac{1}{T} = \frac{1}{T_0} + \frac{1}{\beta} \ln\left(\frac{R}{R_0}\right)$$

With a Beta coefficient of 3950, this formula accounts for the sensor's physical curve and delivers reliable decimal-degree measurements across the entire operating range.

Showing Readings on the I2C Screen

The LCD1602 display features two rows of text with 16 characters on each row. A bare LCD requires up to 16 separate wires to connect directly to a microcontroller. To avoid a tangled mess of wiring, this module includes an I2C backpack board that shrinks the connection down to just two signal wires: SDA (data) and SCL (clock).

LCD1602 16x2 character screen layout with Celsius and Fahrenheit lines
LCD1602 16x2 character screen layout with Celsius and Fahrenheit lines

The diagram above shows the two display lines. Row 0 displays the temperature in Celsius, while Row 1 displays the reading in Fahrenheit. Both rows update once every second as new sensor readings arrive.

Screen lineGrid coordinatesDisplayed formatExample text
Line 1 (Top)Column 0, Row 0Temp : XX.X CTemp : 24.5 C
Line 2 (Bottom)Column 0, Row 1Fahr : XX.X FFahr : 76.1 F

Notice that the code prints extra spaces at the end of each line (" C ") instead of calling lcd.clear(). Calling lcd.clear() wipes the entire screen memory on every update, causing an annoying visual flicker. Printing trailing spaces overwrites older numbers smoothly while keeping the display solid and easy to read.

Connecting the Circuit

Bringing the sensor and display together follows a natural flow: the sensor captures room temperature, the microcontroller processes the reading, and the LCD presents the final value in real time.

Three step diagram showing warm air touching sensor, chip calculating temperature, and LCD displaying 28 degrees Celsius
Three step diagram showing warm air touching sensor, chip calculating temperature, and LCD displaying 28 degrees Celsius

As shown in the flow diagram, each component handles one stage of the measurement pipeline. Wire the thermistor sensor module to provide analog voltage data and the I2C display to present the live results:

Pin Connection Map
NTC Thermistor Module
VCC
→
ESP32-C3 DevKit
3V3 (3.3V Power)
Explanation
Supplies 3.3V power to the thermistor voltage divider.
NTC Thermistor Module
GND
→
ESP32-C3 DevKit
GND
Explanation
Connects to system common ground.
NTC Thermistor Module
OUT (Analog)
→
ESP32-C3 DevKit
GPIO 0
Explanation
Carries analog temperature voltage to ADC1_CH0.

Next, connect the I2C LCD1602 display using its 4-pin interface to provide real-time temperature telemetry:

Pin Connection Map
LCD1602 (I2C Backpack)
VCC
→
ESP32-C3 DevKit
3V3 (3.3V Power)
Explanation
Powers the LCD controller and I2C backpack.
LCD1602 (I2C Backpack)
GND
→
ESP32-C3 DevKit
GND
Explanation
Shared ground return for power and signals.
LCD1602 (I2C Backpack)
SDA (Data)
→
ESP32-C3 DevKit
GPIO 8
Explanation
I2C Serial Data line for transmitting display characters.
LCD1602 (I2C Backpack)
SCL (Clock)
→
ESP32-C3 DevKit
GPIO 9
Explanation
I2C Serial Clock line for bus timing synchronization.

On the ESP32-C3, hardware I2C default pins are GPIO 8 for SDA and GPIO 9 for SCL. Explicitly calling Wire.begin(8, 9) in your setup routine ensures the microcontroller routes its internal I2C peripheral to the correct external pins before communicating with the screen.

Complete Code

Upload the following sketch to your ESP32-C3 DevKit. It initializes the I2C bus, shows a welcome banner, and starts streaming continuous live temperature measurements to the screen and the serial console.

C++ Source
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// ============================================================================
// ESP32-C3 Precision Digital Thermometer with LCD1602
// NTC on GPIO 0 (ADC1_CH0) | I2C LCD on SDA: GPIO 8, SCL: GPIO 9
// ============================================================================
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// I2C LCD configuration (0x27 address, 16 columns, 2 rows)
LiquidCrystal_I2C lcd(0x27, 16, 2);

// NTC analog signal on ADC1_CH0 (GPIO 0)
const int ntcPin = 0;
const float BETA = 3950.0;

void setup() {
  Serial.begin(115200);
  
  // ESP32-C3 default hardware I2C pins: SDA=GPIO 8, SCL=GPIO 9
  Wire.begin(8, 9);
  
  lcd.init();
  lcd.backlight();
  
  lcd.setCursor(0, 0);
  lcd.print("ESP32-C3 Station");
  lcd.setCursor(0, 1);
  lcd.print("Initializing...");
  delay(1200);
  lcd.clear();
}

void loop() {
  // 12-bit ADC reads 0 to 4095 on 3.3V full-scale
  int rawADC = analogRead(ntcPin);
  if (rawADC <= 0) rawADC = 1;
  if (rawADC >= 4095) rawADC = 4094;

  // Beta equation for thermistor conversion
  float celsius = 1.0 / (log(1.0 / (4095.0 / rawADC - 1.0)) / BETA + 1.0 / 298.15) - 273.15;
  float fahrenheit = (celsius * 9.0 / 5.0) + 32.0;

  // Line 1: Celsius reading
  lcd.setCursor(0, 0);
  lcd.print("Temp : ");
  lcd.print(celsius, 1);
  lcd.print(" C   ");

  // Line 2: Fahrenheit reading
  lcd.setCursor(0, 1);
  lcd.print("Fahr : ");
  lcd.print(fahrenheit, 1);
  lcd.print(" F   ");

  // Telemetry output
  Serial.print("ADC: ");
  Serial.print(rawADC);
  Serial.print(" | Temp: ");
  Serial.print(celsius, 2);
  Serial.println(" °C");

  delay(1000);
}

How the Code Works, Part by Part

The sketch coordinates between hardware I2C communications and analog sensor math. Here is what happens in each section of the code.

▸ Libraries and Sensor Settings

The sketch starts by including Wire.h for I2C communication and LiquidCrystal_I2C.h to drive the display. It instantiates the display object at address 0x27 with 16 columns and 2 rows.

C++ Source
1
2
3
4
5
6
7
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27, 16, 2);

const int ntcPin = 0;
const float BETA = 3950.0;

The BETA constant represents the thermal curve value specified by the thermistor manufacturer (typically 3950 for standard 10k NTC sensors).

▸ Initializing the I2C Bus and Display

Inside setup(), the code opens the serial port and assigns the I2C pins. Calling Wire.begin(8, 9) configures GPIO 8 as SDA and GPIO 9 as SCL.

C++ Source
1
2
3
4
5
6
7
8
9
10
11
Wire.begin(8, 9);

lcd.init();
lcd.backlight();

lcd.setCursor(0, 0);
lcd.print("ESP32-C3 Station");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
delay(1200);
lcd.clear();

The display turns on its blue backlight, shows a brief 1.2-second startup banner, and then clears the screen once before entering the main loop.

▸ Converting Sensor Readings to Real Temperatures

The ESP32-C3 features a 12-bit ADC, meaning analogRead(ntcPin) returns a number from 0 to 4095. The code clamps values at 1 and 4094 to avoid mathematical errors like division by zero or taking the logarithm of zero.

C++ Source
1
2
3
4
5
6
int rawADC = analogRead(ntcPin);
if (rawADC <= 0) rawADC = 1;
if (rawADC >= 4095) rawADC = 4094;

float celsius = 1.0 / (log(1.0 / (4095.0 / rawADC - 1.0)) / BETA + 1.0 / 298.15) - 273.15;
float fahrenheit = (celsius * 9.0 / 5.0) + 32.0;

Once the Celsius value is computed, converting to Fahrenheit is done with simple arithmetic: multiply by 9/5 and add 32.

▸ Updating the Screen Without Flickering

The code uses lcd.setCursor(0, 0) to position the cursor at the start of Row 0, then prints the temperature formatted to one decimal place.

C++ Source
1
2
3
4
5
6
7
8
9
lcd.setCursor(0, 0);
lcd.print("Temp : ");
lcd.print(celsius, 1);
lcd.print(" C   ");

lcd.setCursor(0, 1);
lcd.print("Fahr : ");
lcd.print(fahrenheit, 1);
lcd.print(" F   ");

The program repeats this update once every second using delay(1000), providing a steady, readable stream of data.

Fixing Common Problems

If the screen stays dark or the readings seem unusual, consult the troubleshooting guide below.

What you seeLikely causeWhat to try
LCD backlight is on but characters are blankContrast trimmer on I2C backpack is misalignedGently turn the small blue potentiometer on the back of the LCD until characters appear
Program freezes or crashes at startupMissing I2C pin declarationEnsure Wire.begin(8, 9) is called before lcd.init()
Temperature shows extreme negative valuesSensor pin disconnected or shorted to GNDVerify sensor OUT wire connects securely to GPIO 0
Screen flickers or flashes every secondUsing lcd.clear() inside the main loopRemove lcd.clear() and print trailing spaces to overwrite digits cleanly

The most common issue with I2C LCD displays is the contrast potentiometer. If your screen powers on with a blue glow but no letters appear, rotating the contrast screw on the backpack almost always solves the problem immediately.

Try It in the Simulator

Click the Start Simulation (▶) button in the top toolbar to begin. After the initial welcome message, the LCD screen will display real-time room temperature. Click the NTC Temperature Sensor on the canvas to open the temperature slider. Drag the temperature slider up and down to watch both Celsius and Fahrenheit numbers update smoothly on the screen!

Keywords
#ESP32-C3 #NTC #LCD1602 #I2C #Beginner #RISC-V #Temperature
Total word count: 1171 words

Project discussion

Questions, feedback, and community insights
No discussions yet. Be the first to start!