Skip to main content

Working with Arduino

This chapter contains the following sections. Please read as needed:

Arduino Getting Started

New to Arduino ESP32 development and looking for a quick start? We have prepared a comprehensive Getting Started Tutorial for you.

Note: This tutorial uses the ESP32-S3-Zero as a reference example, and all hardware code is based on its pinout. Before you start, we recommend checking the pinout of your development board to ensure the pin configuration is correct.

Setting Up Development Environment

1. Installing and Configuring Arduino IDE

Please refer to the tutorial Installing and Configuring Arduino IDE to download and install the Arduino IDE and add ESP32 support.

Board NameBoard Installation RequirementVersion Requirement
esp32 by Espressif Systems"Install Offline" / "Install Online"3.0.5

2. Installing Libraries

To run the example, you need to install the corresponding library. The example code uses the GFX Library for Arduino library to drive the ST7789V2 display and the Arduino_DriveBus library to drive the CST816T touch controller.

You can click this link to download the example package for the ESP32-S3-Touch-LCD-1.83 development board. The Arduino\libraries directory within this package contains all the necessary library files required for this tutorial.

Library/File NameDescriptionVersionInstallation Method
Arduino_DriveBusCST816 touch controller driver libraryv1.0.1"Install Offline"
GFX Library for ArduinoST7789 display driver graphics libraryv1.4.9"Install Online" or "Install Offline"
SensorLibPCF85063, QMI8658 sensor driver libraryv0.1.6"Install Online" or "Install Offline"
lvglLVGL graphical libraryv8.4.0"Install Online" requires copying the demos folder to src after installation. "Install Offline" is recommended
MylibraryDevelopment board pin macro definitions--"Install Offline"
lv_conf.hLVGL configuration file--"Install Offline"
Version Compatibility Notes

There are strong dependencies between versions of LVGL and its driver libraries. For example, a driver written for LVGL v8 may not be compatible with LVGL v9. To ensure that the examples can be reproduced reliably, it is recommended to use the specific versions listed in the table above. Mixing different versions of libraries may lead to compilation failures or runtime errors.

Installation Steps:

  1. Unzip the downloaded example package.

  2. Copy all folders (Arduino_DriveBus, GFX_Library_for_Arduino, etc.) in the Arduino\libraries directory to the Arduino library folder.

    info

    The path to the Arduino libraries folder is typically: c:\Users\<username>\Documents\Arduino\libraries.

    You can also locate it in the Arduino IDE by going to File > Preferences and checking the "Sketchbook location". The libraries folder is the libraries subfolder within this path.

  3. For other installation methods, please refer to: Arduino Library Management Tutorial.

3. Other Tips

  1. The ESP32-S3-Touch-LCD-1.83 can be directly selected in the Arduino IDE.

    ESP32-S3-Touch-LCD-1.83_Model_selection
  2. The ESP32-S3-Touch-LCD-1.83 uses the ESP32-S3 native USB interface, not UART-to-USB. For serial communication:

    • The printf() function can be used directly;

    • To use the Serial.println() function, additional configuration is required: Enable the "USB CDC On Boot" option in the IDE's Tools menu, or declare an HWCDC object in your code to handle USB serial communication.

      note

      As shown in the figure, set "USB CDC On Boot" in the "Tools" option of the Arduino IDE

      ESP32-S3-Touch-LCD-1.83_parameter_setting

Example

The Arduino examples are located in the Arduino/examples directory of the example package.

ExampleBasic DescriptionDependency Library
01_HelloWorldDemonstrates basic graphics library functions, can also be used to test the basic performance of the display and random text display effectsGFX_Library_for_Arduino
02_Drawing_boardDemonstrates basic graphics library functions, can also be used to test the basic performance of the display and random text display effectsGFX_Library_for_Arduino, Arduino DriveBus
03_GFX_AsciiTablePrints ASCII characters in rows and columns on the screen according to the screen sizeGFX_Library_for_Arduino
04_GFX_ESPWiFiAnalyzerDraws Wi-Fi band signal strength on the ST7789 displayGFX_Library_for_Arduino
05_GFX_ClockA simple ST7789 clock example implementing a clock with simple tick marks and time managementGFX_Library_for_Arduino
06_GFX_PCF85063_simpleTimeDisplays the current timeSensorLib, GFX_Library_for_Arduino
07_LVGL_PCF85063_simpleTimeDisplays current time on the ST7789 display using the PCF85063 RTC module under LVGLLVGL, SensorLib
08_LVGL_QMI8658_uiUses LVGL for graphical display and communicates with the QMI8658 IMU to obtain accelerometer and gyroscope dataLVGL, SensorLib
09_LVGL_ArduinoLVGL demoLVGL, Arduino DriveBus

01_HelloWorld

This example demonstrates how to use the Arduino GFX library and the Arduino DriveBus library to control the ST7789 display, showing basic graphics library functions through dynamically changing text. This code can also be used to test the basic performance of the display and the effect of displaying random text.

Hardware Connection

  • Connect the board to the computer using a USB cable
    ESP32-S3-Touch-LCD-1.83_connect

Code Analysis

  • Display initialization:

    if (!gfx->begin()) {
    USBSerial.println("gfx->begin() failed!");
    }
  • Clear the screen and display text:

    gfx->fillScreen(BLACK);
    gfx->setCursor(10, 10);
    gfx->setTextColor(RED);
    gfx->println("Hello World!");
  • Animated display:

    gfx->setCursor(random(gfx->width()), random(gfx->height()));
    gfx->setTextColor(random(0xffff), random(0xffff));
    gfx->setTextSize(random(6), random(6), random(2));
    gfx->println("Hello World!");

Operation Result

  • This example demonstrates how to use the Arduino GFX library and the Arduino DriveBus library to control the ST7789 display, showing basic graphics library functions through dynamically changing text. This code can also be used to test the basic performance of the display and the effect of displaying random text.
    ESP32-S3-Touch-LCD-1.83_demo_1

02_Drawing_board

  • This example demonstrates how to use the ESP32 to control the CST816 touch controller and TCA9554 GPIO expander via I2C, while using the Arduino GFX library to drive the ST7789 display.

Hardware Connection

  • Connect the board to the computer using a USB cable

Code Analysis

  • Display initialization and brightness fade animation:

    gfx->begin();
    gfx->fillScreen(WHITE);
    for(int i = 0;i <= 255;i++){
    gfx->Display_Brightness(i);
    gfx->setCursor(30, 150);
    gfx->setTextColor(BLUE);
    gfx->setTextSize(4);
    gfx->println("Loading board");
    delay(3);
    }

Operation Result

ESP32-S3-Touch-LCD-1.83_demo_2

03_GFX_AsciiTable

This example shows how to create an ASCII table display effect using the ST7789 display and the Arduino GFX library. Specifically, after initializing the display, the code displays ASCII character indices on the screen according to predefined colors and positions. First, the string "Arduino_GFX AsciiTable example" is printed to the serial monitor via USBSerial. Then indices are displayed in two colors (green and blue) at fixed positions, followed by the full ASCII character table displayed in white on a black background.

Hardware Connection

  • Connect the board to the computer using a USB cable

Code Analysis

  • Initialize the display:

    if (!gfx->begin()) {
    USBSerial.println("gfx->begin() failed!");
    }
  • Calculate rows and columns and label them:

    Based on the display dimensions, it calculates the number of columns and rows that can be displayed. Then, using two loops with different text colors, it prints row and column numbers on the display, making it easy to determine character positions when drawing the ASCII table later.

    int numCols = LCD_WIDTH / 8;
    int numRows = LCD_HEIGHT / 10;

    // Label the line number
    gfx->setTextColor(GREEN);
    for (int x = 0; x < numRows; x++) {
    gfx->setCursor(10 + x * 8, 2);
    gfx->print(x, 16);
    }

    // Label the column number
    gfx->setTextColor(BLUE);
    for (int y = 0; y < numCols; y++) {
    gfx->setCursor(2, 12 + y * 10);
    gfx->print(y, 16);
    }
  • Draws the ASCII character table:

    char c = 0;
    for (int y = 0; y < numRows; y++) {
    for (int x = 0; x < numCols; x++) {
    gfx->drawChar(10 + x * 8, 12 + y * 10, c++, WHITE, BLACK);
    }
    }

Operation Result

ESP32-S3-Touch-LCD-1.83_demo_3

04_GFX_ESPWiFiAnalyzer

This example demonstrates drawing Wi-Fi band signal strength on the ST7789 display, implementing the functionality of a Wi-Fi analyzer.

Hardware Connection

  • Connect the board to the computer using a USB cable

Code Analysis

  • setup():

    • Initializes serial communication;
    • Sets Wi-Fi to station mode and disconnects;
    • Initializes the display, gets screen dimensions and calculates various drawing parameters;
    • Sets the screen background to black and draws the title bar.
  • loop():

    • Scans Wi-Fi networks and retrieves network information, including channel, RSSI, BSSID, and SSID;
    • Counts the number of networks per channel, noise levels, and peak signal strengths;
    • Clears the old graph and draws a new one based on the scan results, including signal strength ellipses and network information text;
    • Prints the number of networks found and the channels with the least noise;
    • Draws the graph baseline and channel numbers;
    • Enters low‑power mode based on conditions.

Operation Result

ESP32-S3-Touch-LCD-1.83_demo_04

05_GFX_Clock

This example demonstrates a simple ST7789 clock example, implementing a clock with simple tick marks and time management.

Hardware Connection

  • Connect the board to the computer using a USB cable
05_GFX_Clock.ino
#include <Arduino.h>
#include "Arduino_GFX_Library.h"
#include "pin_config.h"
#include <Wire.h>
#include "HWCDC.h"

HWCDC USBSerial;
// SensorPCF85063 rtc;

Arduino_DataBus *bus = new Arduino_ESP32SPI(LCD_DC, LCD_CS, LCD_SCK, LCD_MOSI);

Arduino_GFX *gfx = new Arduino_ST7789(bus, LCD_RST /* RST */,
0 /* rotation */, true /* IPS */, LCD_WIDTH, LCD_HEIGHT, 0, 20, 0, 0);

#define BACKGROUND BLACK
#define MARK_COLOR WHITE
#define SUBMARK_COLOR DARKGREY // LIGHTGREY
#define HOUR_COLOR WHITE
#define MINUTE_COLOR BLUE // LIGHTGREY
#define SECOND_COLOR RED

#define SIXTIETH 0.016666667
#define TWELFTH 0.08333333
#define SIXTIETH_RADIAN 0.10471976
#define TWELFTH_RADIAN 0.52359878
#define RIGHT_ANGLE_RADIAN 1.5707963

static uint8_t conv2d(const char *p)
{
uint8_t v = 0;
return (10 * (*p - '0')) + (*++p - '0');
}

static int16_t w, h, center;
static int16_t hHandLen, mHandLen, sHandLen, markLen;
static float sdeg, mdeg, hdeg;
static int16_t osx = 0, osy = 0, omx = 0, omy = 0, ohx = 0, ohy = 0; // Saved H, M, S x & y coords
static int16_t nsx, nsy, nmx, nmy, nhx, nhy; // H, M, S x & y coords
static int16_t xMin, yMin, xMax, yMax; // redraw range
static int16_t hh, mm, ss;
static unsigned long targetTime; // next action time

static int16_t *cached_points;
static uint16_t cached_points_idx = 0;
static int16_t *last_cached_point;

void setup(void)
{
USBSerial.begin(115200);
USBSerial.println("Arduino_GFX Clock example");

// Init Display
if (!gfx->begin())
{
USBSerial.println("gfx->begin() failed!");
}
gfx->fillScreen(BACKGROUND);

pinMode(LCD_BL, OUTPUT);
digitalWrite(LCD_BL, HIGH);

// init LCD constant
w = gfx->width();
h = gfx->height();
if (w < h)
{
center = w / 2;
}
else
{
center = h / 2;
}
hHandLen = center * 3 / 8;
mHandLen = center * 2 / 3;
sHandLen = center * 5 / 6;
markLen = sHandLen / 6;
cached_points = (int16_t *)malloc((hHandLen + 1 + mHandLen + 1 + sHandLen + 1) * 2 * 2);

// Draw 60 clock marks
draw_round_clock_mark(
// draw_square_clock_mark(
center - markLen, center,
center - (markLen * 2 / 3), center,
center - (markLen / 2), center);

hh = conv2d(__TIME__);
mm = conv2d(__TIME__ + 3);
ss = conv2d(__TIME__ + 6);

targetTime = ((millis() / 1000) + 1) * 1000;
}

void loop()
{
unsigned long cur_millis = millis();
if (cur_millis >= targetTime)
{
targetTime += 1000;
ss++; // Advance second
if (ss == 60)
{
ss = 0;
mm++; // Advance minute
if (mm > 59)
{
mm = 0;
hh++; // Advance hour
if (hh > 23)
{
hh = 0;
}
}
}
}

// Pre-compute hand degrees, x & y coords for a fast screen update
sdeg = SIXTIETH_RADIAN * ((0.001 * (cur_millis % 1000)) + ss); // 0-59 (includes millis)
nsx = cos(sdeg - RIGHT_ANGLE_RADIAN) * sHandLen + center;
nsy = sin(sdeg - RIGHT_ANGLE_RADIAN) * sHandLen + center;
if ((nsx != osx) || (nsy != osy))
{
mdeg = (SIXTIETH * sdeg) + (SIXTIETH_RADIAN * mm); // 0-59 (includes seconds)
hdeg = (TWELFTH * mdeg) + (TWELFTH_RADIAN * hh); // 0-11 (includes minutes)
mdeg -= RIGHT_ANGLE_RADIAN;
hdeg -= RIGHT_ANGLE_RADIAN;
nmx = cos(mdeg) * mHandLen + center;
nmy = sin(mdeg) * mHandLen + center;
nhx = cos(hdeg) * hHandLen + center;
nhy = sin(hdeg) * hHandLen + center;

// redraw hands
redraw_hands_cached_draw_and_erase();

ohx = nhx;
ohy = nhy;
omx = nmx;
omy = nmy;
osx = nsx;
osy = nsy;

delay(1);
}
}

void draw_round_clock_mark(int16_t innerR1, int16_t outerR1, int16_t innerR2, int16_t outerR2, int16_t innerR3, int16_t outerR3)
{
float x, y;
int16_t x0, x1, y0, y1, innerR, outerR;
uint16_t c;

for (uint8_t i = 0; i < 60; i++)
{
if ((i % 15) == 0)
{
innerR = innerR1;
outerR = outerR1;
c = MARK_COLOR;
}
else if ((i % 5) == 0)
{
innerR = innerR2;
outerR = outerR2;
c = MARK_COLOR;
}
else
{
innerR = innerR3;
outerR = outerR3;
c = SUBMARK_COLOR;
}

mdeg = (SIXTIETH_RADIAN * i) - RIGHT_ANGLE_RADIAN;
x = cos(mdeg);
y = sin(mdeg);
x0 = x * outerR + center;
y0 = y * outerR + center;
x1 = x * innerR + center;
y1 = y * innerR + center;

gfx->drawLine(x0, y0, x1, y1, c);
}
}

void draw_square_clock_mark(int16_t innerR1, int16_t outerR1, int16_t innerR2, int16_t outerR2, int16_t innerR3, int16_t outerR3)
{
float x, y;
int16_t x0, x1, y0, y1, innerR, outerR;
uint16_t c;

for (uint8_t i = 0; i < 60; i++)
{
if ((i % 15) == 0)
{
innerR = innerR1;
outerR = outerR1;
c = MARK_COLOR;
}
else if ((i % 5) == 0)
{
innerR = innerR2;
outerR = outerR2;
c = MARK_COLOR;
}
else
{
innerR = innerR3;
outerR = outerR3;
c = SUBMARK_COLOR;
}

if ((i >= 53) || (i < 8))
{
x = tan(SIXTIETH_RADIAN * i);
x0 = center + (x * outerR);
y0 = center + (1 - outerR);
x1 = center + (x * innerR);
y1 = center + (1 - innerR);
}
else if (i < 23)
{
y = tan((SIXTIETH_RADIAN * i) - RIGHT_ANGLE_RADIAN);
x0 = center + (outerR);
y0 = center + (y * outerR);
x1 = center + (innerR);
y1 = center + (y * innerR);
}
else if (i < 38)
{
x = tan(SIXTIETH_RADIAN * i);
x0 = center - (x * outerR);
y0 = center + (outerR);
x1 = center - (x * innerR);
y1 = center + (innerR);
}
else if (i < 53)
{
y = tan((SIXTIETH_RADIAN * i) - RIGHT_ANGLE_RADIAN);
x0 = center + (1 - outerR);
y0 = center - (y * outerR);
x1 = center + (1 - innerR);
y1 = center - (y * innerR);
}
gfx->drawLine(x0, y0, x1, y1, c);
}
}

void redraw_hands_cached_draw_and_erase()
{
gfx->startWrite();
draw_and_erase_cached_line(center, center, nsx, nsy, SECOND_COLOR, cached_points, sHandLen + 1, false, false);
draw_and_erase_cached_line(center, center, nhx, nhy, HOUR_COLOR, cached_points + ((sHandLen + 1) * 2), hHandLen + 1, true, false);
draw_and_erase_cached_line(center, center, nmx, nmy, MINUTE_COLOR, cached_points + ((sHandLen + 1 + hHandLen + 1) * 2), mHandLen + 1, true, true);
gfx->endWrite();
}

void draw_and_erase_cached_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t color, int16_t *cache, int16_t cache_len, bool cross_check_second, bool cross_check_hour)
{
#if defined(ESP8266)
yield();
#endif
bool steep = _diff(y1, y0) > _diff(x1, x0);
if (steep)
{
_swap_int16_t(x0, y0);
_swap_int16_t(x1, y1);
}

int16_t dx, dy;
dx = _diff(x1, x0);
dy = _diff(y1, y0);

int16_t err = dx / 2;
int8_t xstep = (x0 < x1) ? 1 : -1;
int8_t ystep = (y0 < y1) ? 1 : -1;
x1 += xstep;
int16_t x, y, ox, oy;
for (uint16_t i = 0; i <= dx; i++)
{
if (steep)
{
x = y0;
y = x0;
}
else
{
x = x0;
y = y0;
}
ox = *(cache + (i * 2));
oy = *(cache + (i * 2) + 1);
if ((x == ox) && (y == oy))
{
if (cross_check_second || cross_check_hour)
{
write_cache_pixel(x, y, color, cross_check_second, cross_check_hour);
}
}
else
{
write_cache_pixel(x, y, color, cross_check_second, cross_check_hour);
if ((ox > 0) || (oy > 0))
{
write_cache_pixel(ox, oy, BACKGROUND, cross_check_second, cross_check_hour);
}
*(cache + (i * 2)) = x;
*(cache + (i * 2) + 1) = y;
}
if (err < dy)
{
y0 += ystep;
err += dx;
}
err -= dy;
x0 += xstep;
}
for (uint16_t i = dx + 1; i < cache_len; i++)
{
ox = *(cache + (i * 2));
oy = *(cache + (i * 2) + 1);
if ((ox > 0) || (oy > 0))
{
write_cache_pixel(ox, oy, BACKGROUND, cross_check_second, cross_check_hour);
}
*(cache + (i * 2)) = 0;
*(cache + (i * 2) + 1) = 0;
}
}

void write_cache_pixel(int16_t x, int16_t y, int16_t color, bool cross_check_second, bool cross_check_hour)
{
int16_t *cache = cached_points;
if (cross_check_second)
{
for (uint16_t i = 0; i <= sHandLen; i++)
{
if ((x == *(cache++)) && (y == *(cache)))
{
return;
}
cache++;
}
}
if (cross_check_hour)
{
cache = cached_points + ((sHandLen + 1) * 2);
for (uint16_t i = 0; i <= hHandLen; i++)
{
if ((x == *(cache++)) && (y == *(cache)))
{
return;
}
cache++;
}
}
gfx->writePixel(x, y, color);
}

Code Analysis

  • Drawing hour, minute, and second hands:

    void redraw_hands_cached_draw_and_erase() {
    gfx->startWrite();
    draw_and_erase_cached_line(center, center, nsx, nsy, SECOND_COLOR, cached_points, sHandLen + 1, false, false);
    draw_and_erase_cached_line(center, center, nhx, nhy, HOUR_COLOR, cached_points + ((sHandLen + 1) * 2), hHandLen + 1, true, false);
    draw_and_erase_cached_line(center, center, nmx, nmy, MINUTE_COLOR, cached_points + ((sHandLen + 1 + hHandLen + 1) * 2), mHandLen + 1, true, true);
    gfx->endWrite();
    }

Operation Result

ESP32-S3-Touch-LCD-1.83_demo_05

06_GFX_PCF85063_simpleTime

This example demonstrates using the PCF85063 RTC module to display the current time on the ST7789 display. It retrieves the time every second and updates the display only when the time changes.

Hardware Connection

  • Connect the board to the computer using a USB cable

Code Analysis

  • loop():
    • First, get the current time. If the current time is different from the previously displayed time, do the following:
    • Clear the area where the previous time was displayed by filling a rectangle, so that overlapping does not occur when updating the time.
    • Set the text color to black and the text size to 3.
    • Call getCenteredX to calculate the X coordinate for centering the current time string on the screen.
    • Set the cursor position and print the current time string, updating the time display.
    • Copy the current time string to previousTimeString for the next comparison.

Operation Result

ESP32-S3-Touch-LCD-1.83_demo_06

07_LVGL_PCF85063_simpleTime

This example demonstrates using the PCF85063 RTC module under LVGL to display the current time on the ST7789 display. It retrieves the time every second and updates the display only when the time changes, providing a smoother time refresh effect.

Hardware Connection

  • Connect the board to the computer using a USB cable

Code Analysis

  • setup():

    • Initializes serial communication at 115200 baud for possible serial debugging;
    • Attempts to connect to the PCF85063 real-time clock chip; if it fails, enters an infinite loop;
    • Sets the initial RTC time to 2025-10-23 15:23:49;
    • Initializes the display and sets screen brightness;
    • Initializes LVGL and registers a log output function (if logging is enabled);
    • Configures the LVGL display driver and drawing buffer, and initializes a dummy input device driver;
    • Creates a timer to periodically trigger LVGL tick updates;
    • Creates a label and sets its initial text to "Initializing...".
  • loop():

  • Calls lv_timer_handler to let LVGL handle GUI tasks;

  • Every second, checks whether the time has been updated; if so, retrieves the current time from the RTC, outputs it via serial, formats it as a string, updates the label's text, and sets the label's font to lv_font_montserrat_40.

Operation Result

ESP32-S3-Touch-LCD-1.83_demo_07

08_LVGL_QMI8658_ui

  • This example demonstrates using LVGL for graphical display and communicating with the QMI8658 IMU to obtain accelerometer and gyroscope data.

Hardware Connection

  • Connect the board to the computer using a USB cable

Code Analysis

  • my_disp_flush():

    • This function is the refresh funtion of LVGL display driver. It is responsible for refreshing the LVGL drawing buffer content to the display screen;
    • Depending on the color format setting, it calls the appropriate gfx object function to draw the bitmap to the specified area.
    • Finally, it notifies LVGL that the display refreshing is complete.
  • loop():

    • Calls lv_timer_handler to let LVGL handle GUI tasks;
    • Checks whether new data is ready from the qmi (QMI8658 sensor object). If so, attempts to retrieve accelerometer data and gyroscope data and outputs them via serial;
    • Simultaneously, updates the LVGL chart with the accelerometer data to display real‑time changes in acceleration on the three axes;
    • Uses delay(20) to increase the data polling frequency, ensuring timely retrieval of sensor data and display updates.

Operation Result

ESP32-S3-Touch-LCD-1.83_demo_08

09_LVGL_Arduino

This example demonstrates the LVGL Widgets demo, achieving a frame rate of 20–30 FPS in dynamic states.

Hardware Connection

  • Connect the board to the computer using a USB cable

Operation Result

  • This example demonstrates the LVGL Widgets example, achieving a frame rate of 20–30 FPS in dynamic states.

    Example 11 illustration

  • When developing with the LVGL framework, you can refer to the component documentation provided by the official LVGL documentation: LVGL8.3 Documents

  • Below is an actual LVGL component exploration case in the Arduino IDE:

    Example 11 illustration 2