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.

2. Installing Libraries

To run the demo, you need to install the corresponding library. You can click this link to download the demo package for the ESP32-S3-LCD-0.85 development board. The Arduino\libraries directory within this package contains all the necessary library files required for this tutorial.

Library or File NameDescriptionVersionInstallation Method
lvglLVGL graphics libraryv8.4.0 or v9.3.0Via Library Manager or manual install
GFX_Library_for_ArduinoGFX graphics libraryv1.6.0Via Library Manager or manual install
U8g2Graphics display libraryv2.35.30Via Library Manager or manual install
FastLEDRGB LED driver libraryv3.10.1Via Library Manager or manual install
ESP32-audioI2S-masterAudio processing libraryv3.4.0Via Library Manager or manual install
OneButtonButton libraryv2.6.1Via Library Manager or manual install
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 demo package.

  2. Copy all folders (ESP32-audioI2S-master, GFX_Library_for_Arduino, etc.) from its Arduino\libraries directory to the Arduino libraries 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.

Installation Instructions for the ESP32-S3-Touch-LCD-1.54 Required Board

Board NameInstallation RequirementVersion Requirement
ESP32 by Espressif Systems"Install Offline" / "Install Online"3.2.0

3. Other Tips

For the ESP32-S3-LCD-0.85, you need to select and configure the board.

  1. Select the ESP32S3 Dev Module for ESP32-S3-LCD-0.85.
  2. Select the USB port.
  3. The ESP32-S3-LCD-0.85 uses the ESP32-S3 native USB interface, not a UART-to-USB bridge. 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.

  4. Select 8MB Flash
  5. Select a Partition Table of the appropriate size

Demo

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

DemoBasic DescriptionDependency Library
01_audio_outRead audio files from TF card and play themESP32-audioI2S-master
02_button_example Button testOneButton
03_ws2812b_exampleTest RGB LEDFastLED
04_gfx_helloworldDisplay HelloWorld on the screenGFX_Library_for_Arduino
05_esp_wifi_analyzerDisplay WiFi signal strength on the screenGFX_Library_for_Arduino
06_gfx_u8g2_fontDisplay text in multiple languages by loading fontsGFX_Library_for_Arduino, U8g2
07_sd_card_testTest TF card read/write---
08_lvgl_example_v8lvgl v8.4.0 demoGFX_Library_for_Arduino, lvgl
09_lvgl_example_v9lvgl v9.3.0 demoGFX_Library_for_Arduino, lvgl

01_audio_out

This example demonstrates the ESP32-S3-LCD-0.85 reading an audio file from a TF card and playing it through the speaker. The screen shows no output. It supports formats like MP3, AAC, WAV, etc.

Code

01_audio_out.ino

#include "Arduino.h"
#include "WiFiMulti.h"
#include "Audio.h"
#include "SD_MMC.h"
#include "FS.h"

#include "es8311.h"
#include "esp_check.h"
#include "Wire.h"

#define PA_CTRL 7
#define I2S_MCLK 8
#define I2S_BCLK 9
#define I2S_DOUT 12
#define I2S_LRC 10

#define I2C_SDA 42
#define I2C_SCL 41

#define EXAMPLE_SAMPLE_RATE (16000)
#define EXAMPLE_MCLK_MULTIPLE (256) // If not using 24-bit data width, 256 should be enough
#define EXAMPLE_MCLK_FREQ_HZ (EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE)
#define EXAMPLE_VOICE_VOLUME (75)

Audio audio;
WiFiMulti wifiMulti;
// #define ENABLE_WIFI
String ssid = "waveshare";
String password = "12345678";

int clk = 16;
int cmd = 15;
int d0 = 17;
int d1 = 18;
int d2 = 13;
int d3 = 14;

static esp_err_t es8311_codec_init(void) {

es8311_handle_t es_handle = es8311_create(I2C_NUM_0, ES8311_ADDRRES_0);
ESP_RETURN_ON_FALSE(es_handle, ESP_FAIL, TAG, "es8311 create failed");
const es8311_clock_config_t es_clk = {
.mclk_inverted = false,
.sclk_inverted = false,
.mclk_from_mclk_pin = true,
.mclk_frequency = EXAMPLE_MCLK_FREQ_HZ,
.sample_frequency = EXAMPLE_SAMPLE_RATE
};

ESP_ERROR_CHECK(es8311_init(es_handle, &es_clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16));
ESP_RETURN_ON_ERROR(es8311_sample_frequency_config(es_handle, EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE, EXAMPLE_SAMPLE_RATE), TAG, "set es8311 sample frequency failed");
ESP_RETURN_ON_ERROR(es8311_voice_volume_set(es_handle, EXAMPLE_VOICE_VOLUME, NULL), TAG, "set es8311 volume failed");
ESP_RETURN_ON_ERROR(es8311_microphone_config(es_handle, false), TAG, "set es8311 microphone failed");

return ESP_OK;
}

void setup() {
Serial.begin(115200);
Wire.begin(I2C_SDA, I2C_SCL);
pinMode(PA_CTRL, OUTPUT);
digitalWrite(PA_CTRL, HIGH);
es8311_codec_init();
if (!SD_MMC.setPins(clk, cmd, d0, d1, d2, d3)) {
Serial.println("Pin change failed!");
return;
}
if (!SD_MMC.begin()) {
Serial.println("Card Mount Failed");
return;
}
#ifdef ENABLE_WIFI
WiFi.mode(WIFI_STA);
wifiMulti.addAP(ssid.c_str(), password.c_str());
wifiMulti.run();
if (WiFi.status() != WL_CONNECTED) {
WiFi.disconnect(true);
wifiMulti.run();
}
#endif
audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT,I2S_MCLK);
audio.setVolume(21); // 0...21

#ifdef ENABLE_WIFI
// audio.connecttohost("http://www.wdr.de/wdrlive/media/einslive.m3u");
// audio.connecttohost("http://somafm.com/wma128/missioncontrol.asx"); // asx
// audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.aac"); // 128k aac
// audio.connecttohost("http://mp3.ffh.de/radioffh/hqlivestream.mp3"); // 128k mp3
#else
audio.connecttoFS(SD_MMC, "music/1.mp3");
#endif
}

void loop() {
vTaskDelay(1);
audio.loop();
if (Serial.available()) { // put streamURL in serial monitor
audio.stopSong();
String r = Serial.readString();
r.trim();
if (r.length() > 5) audio.connecttohost(r.c_str());
log_i("free heap=%i", ESP.getFreeHeap());
}
}

// optional
void audio_info(const char *info) {
Serial.print("info ");
Serial.println(info);
}
void audio_id3data(const char *info) { //id3 metadata
Serial.print("id3data ");
Serial.println(info);
}
void audio_eof_mp3(const char *info) { //end of file
Serial.print("eof_mp3 ");
Serial.println(info);
}
void audio_showstation(const char *info) {
Serial.print("station ");
Serial.println(info);
}
void audio_showstreamtitle(const char *info) {
Serial.print("streamtitle ");
Serial.println(info);
}
void audio_bitrate(const char *info) {
Serial.print("bitrate ");
Serial.println(info);
}
void audio_commercial(const char *info) { //duration in sec
Serial.print("commercial ");
Serial.println(info);
}
void audio_icyurl(const char *info) { //homepage
Serial.print("icyurl ");
Serial.println(info);
}
void audio_lasthost(const char *info) { //stream URL played
Serial.print("lasthost ");
Serial.println(info);
}

Code Analysis

  • Initialize the TF card:

      if (!SD_MMC.setPins(clk, cmd, d0, d1, d2, d3)) {
    Serial.println("Pin change failed!");
    return;
    }
    if (!SD_MMC.begin()) {
    Serial.println("Card Mount Failed");
    return;
    }
  • Set I2S pins and volume:

    audio.setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT,I2S_MCLK);
    audio.setVolume(21); // 0...21
  • Set the audio file to play:

    audio.connecttoFS(SD_MMC, "music/1.mp3");

Operation Result

  • The device will play auido directly without showing content on the screen

02_button_example

This demo demonstrates how to use the OneButton library to read button states such as single click, double click, and long press, and print them via the serial monitor.

Code

02_button_example.ino
#include "OneButton.h"

#define PIN_INPUT1 0
#define PIN_INPUT2 5
#define PIN_INPUT3 4

// Setup a new OneButton on pin PIN_INPUT1.
OneButton button1(PIN_INPUT1, true);
// Setup a new OneButton on pin PIN_INPUT2.
OneButton button2(PIN_INPUT2, true);
// Setup a new OneButton on pin PIN_INPUT3.
OneButton button3(PIN_INPUT3, true);

// setup code here, to run once:
void setup() {
// Setup the Serial port. see http://arduino.cc/en/Serial/IfSerial
Serial.begin(115200);
while (!Serial){
; // wait for serial port to connect. Needed for Leonardo only
}
// link the button 1 functions.
button1.attachClick(click1);
button1.attachDoubleClick(doubleclick1);
button1.attachLongPressStart(longPressStart1);
button1.attachLongPressStop(longPressStop1);
button1.attachDuringLongPress(longPress1);

// link the button 2 functions.
button2.attachClick(click2);
button2.attachDoubleClick(doubleclick2);
button2.attachLongPressStart(longPressStart2);
button2.attachLongPressStop(longPressStop2);
button2.attachDuringLongPress(longPress2);

// link the button 3 functions.
button3.attachClick(click3);
button3.attachDoubleClick(doubleclick3);
button3.attachLongPressStart(longPressStart3);
button3.attachLongPressStop(longPressStop3);
button3.attachDuringLongPress(longPress3);

} // setup

// main code here, to run repeatedly:
void loop() {
// keep watching the push buttons:
button1.tick();
button2.tick();
button3.tick();

// You can implement other code in here or just wait a while
delay(10);
} // loop

// ----- button 1 callback functions

// This function will be called when the button1 was pressed 1 time (and no 2. button press followed).
void click1() {
Serial.println("Button 1 click.");
} // click1

// This function will be called when the button1 was pressed 2 times in a short timeframe.
void doubleclick1() {
Serial.println("Button 1 doubleclick.");
} // doubleclick1

// This function will be called once, when the button1 is pressed for a long time.
void longPressStart1() {
Serial.println("Button 1 longPress start");
} // longPressStart1

// This function will be called often, while the button1 is pressed for a long time.
void longPress1() {
Serial.println("Button 1 longPress...");
} // longPress1

// This function will be called once, when the button1 is released after beeing pressed for a long time.
void longPressStop1() {
Serial.println("Button 1 longPress stop");
} // longPressStop1

// ... and the same for button 2:

void click2() {
Serial.println("Button 2 click.");
} // click2

void doubleclick2() {
Serial.println("Button 2 doubleclick.");
} // doubleclick2

void longPressStart2() {
Serial.println("Button 2 longPress start");
} // longPressStart2

void longPress2() {
Serial.println("Button 2 longPress...");
} // longPress2

void longPressStop2() {
Serial.println("Button 2 longPress stop");
} // longPressStop2

// ... and the same for button 3:

void click3() {
Serial.println("Button 3 click.");
} // click3

void doubleclick3() {
Serial.println("Button 3 doubleclick.");
} // doubleclick3

void longPressStart3() {
Serial.println("Button 3 longPress start");
} // longPressStart3

void longPress3() {
Serial.println("Button 3 longPress...");
} // longPress3

void longPressStop3() {
Serial.println("Button 3 longPress stop");
} // longPressStop3

// End

Code Analysis

  • Bind callback functions:

    button1.attachClick(click1);
    button1.attachDoubleClick(doubleclick1);
    button1.attachLongPressStart(longPressStart1);
    button1.attachLongPressStop(longPressStop1);
    button1.attachDuringLongPress(longPress1);

Operation Result

  • The screen shows no output
  • Button information is printed to the serial monitor

03_ws2812b_example

This example uses the FastLED library to drive the ws2812b RGB LED on the ESP32-S3-LCD-0.85, changing to a random color every 500ms.

Code

03_ws2812b_example.ino
#include <FastLED.h>

// How many LEDs in your strip?
#define NUM_LEDS 8
#define DATA_PIN 48

// Define the array of leds
CRGB leds[NUM_LEDS];

void setup() {
FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS); // GRB ordering is typical
FastLED.setBrightness(100); // 0-255
for(int i = 0; i < NUM_LEDS; i++){
leds[i] = CRGB::Red;
}
FastLED.show();

delay(2000);
}

void loop() {
for(int i = 0; i < NUM_LEDS; i++){
leds[i] = random(0xffffff);
}
FastLED.show();
delay(500);
}

Code Analysis

  • Initialize ws2812b and set brightness:

    FastLED.addLeds<WS2812B, DATA_PIN, RGB>(leds, NUM_LEDS);  // GRB ordering is typical
    FastLED.setBrightness(100); // 0-255

Operation Result

  • The screen shows no output
  • The RGB LED changes to a random color every 500ms
  • Opening the serial monitor shows printed accelerometer and gyroscope data for the x, y, and z axes

04_gfx_helloworld

This demo demonstrates the ESP32-S3-LCD-0.85 using the GFX_Library_for_Arduino library to drive the screen and display "HelloWorld” on the screen.

Code

04_gfx_helloworld.ino
#include <Arduino_GFX_Library.h>

#define GFX_BL 46 // default backlight pin, you may replace DF_GFX_BL to actual backlight pin

/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_ESP32SPI(45 /* DC */, 21 /* CS */, 38 /* SCK */, 39 /* MOSI */, GFX_NOT_DEFINED /* MISO */);

/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
Arduino_GFX *gfx = new Arduino_GC9107(bus, 40 /* RST */, 0 /* rotation */, true /* IPS */);

setup() {
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif

Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX Hello World example");

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

#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif

gfx->setCursor(10, 10);
gfx->setTextColor(RGB565_RED);
gfx->println("Hello World!");

delay(5000); // 5 seconds
}

void loop()
{
gfx->setCursor(random(gfx->width()), random(gfx->height()));
gfx->setTextColor(random(0xffff), random(0xffff));
gfx->setTextSize(random(6) /* x scale */, random(6) /* y scale */, random(2) /* pixel_margin */);
gfx->println("Hello World!");

delay(1000); // 1 second
}

Code Analysis

  • Configure the screen interface and screen resolution, etc.
    Arduino_DataBus *bus = new Arduino_ESP32SPI(45 /* DC */, 21 /* CS */, 38 /* SCK */, 39 /* MOSI */, -1 /* MISO */);
    Arduino_GFX *gfx = new Arduino_ST7789(
    bus, 40 /* RST */, 0 /* rotation */, true, 240, 240);

Operation Result

  • The screen displays as follows:

05_esp_wifi_analyzer

This demo demonstrates the ESP32-S3-LCD-0.85 using a GFX_Library_for_Arduino library to display the signal strength of the WiFi band

Code

05_esp_wifi_analyzer.ino
/*
* ESP WiFi Analyzer
* Require ESP8266/ESP32 board support.
*/

// POWER SAVING SETTING
#define SCAN_INTERVAL 3000
// #define SCAN_COUNT_SLEEP 3
// #define LCD_PWR_PIN 14

#include <Arduino_GFX_Library.h>

#define GFX_BL 46 // default backlight pin, you may replace DF_GFX_BL to actual backlight pin

/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_ESP32SPI(45 /* DC */, 21 /* CS */, 38 /* SCK */, 39 /* MOSI */, GFX_NOT_DEFINED /* MISO */);

/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
Arduino_GFX *gfx = new Arduino_GC9107(bus, 40 /* RST */, 0 /* rotation */, true /* IPS */);

#if defined(ESP32)
#include "WiFi.h"
#else
#include "ESP8266WiFi.h"
#define log_i(format, ...) Serial.printf(format, ##__VA_ARGS__)
#endif

int16_t w, h, text_size, banner_height, graph_baseline, graph_height, channel_width, signal_width;

// RSSI RANGE
#define RSSI_CEILING -40
#define RSSI_FLOOR -100

// Channel color mapping from channel 1 to 14
uint16_t channel_color[] = {
RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_GREEN, RGB565_CYAN, RGB565_BLUE, RGB565_MAGENTA,
RGB565_RED, RGB565_ORANGE, RGB565_YELLOW, RGB565_GREEN, RGB565_CYAN, RGB565_BLUE, RGB565_MAGENTA};

uint8_t scan_count = 0;

void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif

Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX ESP WiFi Analyzer example");

// Set WiFi to station mode and disconnect from an AP if it was previously connected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(100);

#if defined(LCD_PWR_PIN)
pinMode(LCD_PWR_PIN, OUTPUT); // sets the pin as output
digitalWrite(LCD_PWR_PIN, HIGH); // power on
#endif

#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif

// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
w = gfx->width();
h = gfx->height();
text_size = (h < 200) ? 1 : 2;
banner_height = text_size * 3 * 4;
graph_baseline = h - 20; // minus 2 text lines
graph_height = graph_baseline - banner_height - 30; // minus 3 text lines
channel_width = w / 17;
signal_width = channel_width * 2;

// init banner
gfx->setTextSize(text_size);
gfx->fillScreen(RGB565_BLACK);
gfx->setTextColor(RGB565_RED);
gfx->setCursor(0, 0);
gfx->print("ESP");
gfx->setTextColor(RGB565_WHITE);
gfx->print(" WiFi Analyzer");

#ifdef CANVAS
gfx->flush();
#endif
}

bool matchBssidPrefix(uint8_t *a, uint8_t *b)
{
for (uint8_t i = 0; i < 5; i++)
{ // only compare first 5 bytes
if (a[i] != b[i])
{
return false;
}
}
return true;
}

void loop()
{
uint8_t ap_count_list[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int32_t noise_list[] = {RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR};
int32_t peak_list[] = {RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR, RSSI_FLOOR};
int16_t peak_id_list[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
int32_t channel;
int16_t idx;
int32_t rssi;
uint8_t *bssid;
String ssid;
uint16_t color;
int16_t height, offset, text_width;

// WiFi.scanNetworks will return the number of networks found
#if defined(ESP32)
int n = WiFi.scanNetworks(false /* async */, true /* show_hidden */, true /* passive */, 500 /* max_ms_per_chan */);
#else
int n = WiFi.scanNetworks(false /* async */, true /* show_hidden */);
#endif

// clear old graph
gfx->fillRect(0, banner_height, w, h - banner_height, RGB565_BLACK);
gfx->setTextSize(1);

if (n == 0)
{
gfx->setTextColor(RGB565_WHITE);
gfx->setCursor(0, banner_height);
gfx->println("no networks found");
}
else
{
for (int i = 0; i < n; i++)
{
channel = WiFi.channel(i);
idx = channel - 1;
rssi = WiFi.RSSI(i);
bssid = WiFi.BSSID(i);

// channel peak stat
if (peak_list[idx] < rssi)
{
peak_list[idx] = rssi;
peak_id_list[idx] = i;
}

// check signal come from same AP
bool duplicate_SSID = false;
for (int j = 0; j < i; j++)
{
if ((WiFi.channel(j) == channel) && matchBssidPrefix(WiFi.BSSID(j), bssid))
{
duplicate_SSID = true;
break;
}
}

if (!duplicate_SSID)
{
ap_count_list[idx]++;

// noise stat
int32_t noise = rssi - RSSI_FLOOR;
noise *= noise;
if (channel > 4)
{
noise_list[idx - 4] += noise;
}
if (channel > 3)
{
noise_list[idx - 3] += noise;
}
if (channel > 2)
{
noise_list[idx - 2] += noise;
}
if (channel > 1)
{
noise_list[idx - 1] += noise;
}
noise_list[idx] += noise;
if (channel < 14)
{
noise_list[idx + 1] += noise;
}
if (channel < 13)
{
noise_list[idx + 2] += noise;
}
if (channel < 12)
{
noise_list[idx + 3] += noise;
}
if (channel < 11)
{
noise_list[idx + 4] += noise;
}
}
}

// plot found WiFi info
for (int i = 0; i < n; i++)
{
channel = WiFi.channel(i);
idx = channel - 1;
rssi = WiFi.RSSI(i);
color = channel_color[idx];
height = constrain(map(rssi, RSSI_FLOOR, RSSI_CEILING, 1, graph_height), 1, graph_height);
offset = (channel + 1) * channel_width;

// trim rssi with RSSI_FLOOR
if (rssi < RSSI_FLOOR)
{
rssi = RSSI_FLOOR;
}

// plot chart
// gfx->drawLine(offset, graph_baseline - height, offset - signal_width, graph_baseline + 1, color);
// gfx->drawLine(offset, graph_baseline - height, offset + signal_width, graph_baseline + 1, color);
gfx->startWrite();
gfx->writeEllipseHelper(offset, graph_baseline + 1, signal_width, height, 0b0011, color);
gfx->endWrite();

if (i == peak_id_list[idx])
{
// Print SSID, signal strengh and if not encrypted
String ssid = WiFi.SSID(i);
if (ssid.length() == 0)
{
ssid = WiFi.BSSIDstr(i);
}
text_width = (ssid.length() + 6) * 6;
if (text_width > w)
{
offset = 0;
}
else
{
offset -= signal_width;
if ((offset + text_width) > w)
{
offset = w - text_width;
}
}
gfx->setTextColor(color);
gfx->setCursor(offset, graph_baseline - 10 - height);
gfx->print(ssid);
gfx->print('(');
gfx->print(rssi);
gfx->print(')');
#if defined(ESP32)
if (WiFi.encryptionType(i) == WIFI_AUTH_OPEN)
#else
if (WiFi.encryptionType(i) == ENC_TYPE_NONE)
#endif
{
gfx->print('*');
}
}
}
}

// print WiFi stat
gfx->setTextColor(RGB565_WHITE);
gfx->setCursor(0, banner_height);
gfx->print(n);
gfx->print(" networks found, lesser noise channels: ");
bool listed_first_channel = false;
int32_t min_noise = noise_list[0]; // init with channel 1 value
for (channel = 2; channel <= 11; channel++) // channels 12-14 may not available
{
idx = channel - 1;
log_i("min_noise: %d, noise_list[%d]: %d", min_noise, idx, noise_list[idx]);
if (noise_list[idx] < min_noise)
{
min_noise = noise_list[idx];
}
}

for (channel = 1; channel <= 11; channel++) // channels 12-14 may not available
{
idx = channel - 1;
// check channel with min noise
if (noise_list[idx] == min_noise)
{
if (!listed_first_channel)
{
listed_first_channel = true;
}
else
{
gfx->print(", ");
}
gfx->print(channel);
}
}

// draw graph base axle
gfx->drawFastHLine(0, graph_baseline, gfx->width(), RGB565_WHITE);
for (channel = 1; channel <= 14; channel++)
{
idx = channel - 1;
offset = (channel + 1) * channel_width;
gfx->setTextColor(channel_color[idx]);
gfx->setCursor(offset - ((channel < 10) ? 3 : 6), graph_baseline + 2);
gfx->print(channel);
if (ap_count_list[idx] > 0)
{
gfx->setCursor(offset - ((ap_count_list[idx] < 10) ? 9 : 12), graph_baseline + 8 + 2);
gfx->print('{');
gfx->print(ap_count_list[idx]);
gfx->print('}');
}
}

#ifdef CANVAS
gfx->flush();
#endif

// Wait a bit before scanning again
delay(SCAN_INTERVAL);

#if defined(SCAN_COUNT_SLEEP)
// POWER SAVING
if (++scan_count >= SCAN_COUNT_SLEEP)
{
#if defined(LCD_PWR_PIN)
pinMode(LCD_PWR_PIN, INPUT); // disable pin
#endif

#if defined(GFX_BL)
pinMode(GFX_BL, INPUT); // disable pin
#endif

#if defined(ESP32)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_36, LOW);
esp_deep_sleep_start();
#else
ESP.deepSleep(0);
#endif
}
#endif // defined(SCAN_COUNT_SLEEP)
}

Operation Result

  • The screen displays as follows:

06_gfx_u8g2_font

This demo demonstrates how ESP32-S3-LCD-0.85 uses GFX_Library_for_Arduino libraries to load font libraries to display them in various languages

Code

06_gfx_u8g2_font.ino

#include <U8g2lib.h>
#include <Arduino_GFX_Library.h>

#define GFX_BL 46 // default backlight pin, you may replace DF_GFX_BL to actual backlight pin

/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_ESP32SPI(45 /* DC */, 21 /* CS */, 38 /* SCK */, 39 /* MOSI */, GFX_NOT_DEFINED /* MISO */);

/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
Arduino_GFX *gfx = new Arduino_GC9107(bus, 40 /* RST */, 0 /* rotation */, true /* IPS */);

void setup(void)
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif

Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX U8g2 Font Print UTF8 example");

// Init Display
if (!gfx->begin())
{
Serial.println("gfx->begin() failed!");
}
gfx->fillScreen(RGB565_BLACK);
gfx->setUTF8Print(true); // enable UTF8 support for the Arduino print() function

#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif

int16_t x1, y1;
uint16_t w, h;

/* U8g2 font list: https://github.com/olikraus/u8g2/wiki/fntlistall */
/* U8g2 Unifont list: https://github.com/olikraus/u8g2/wiki/fntgrpunifont */
gfx->setFont(u8g2_font_unifont_tr);
gfx->setTextColor(RGB565_RED);
gfx->setCursor(0, 16);
// gfx->getTextBounds("Hello World!", 1, 16, &x1, &y1, &w, &h);
// gfx->drawRect(x1 - 1, y1 - 1, w + 2, h + 2, RGB565_RED);
gfx->println("Hello World!");

gfx->setFont(u8g2_font_unifont_t_polish);
gfx->setTextColor(RGB565_YELLOW);
gfx->setCursor(0, 36);
// gfx->getTextBounds("Witaj świecie!", 1, 36, &x1, &y1, &w, &h);
// gfx->drawRect(x1 - 1, y1 - 1, w + 2, h + 2, RGB565_RED);
gfx->println("Witaj świecie!");

gfx->setFont(u8g2_font_unifont_t_vietnamese1);
gfx->setTextColor(RGB565_GREEN);
gfx->setCursor(0, 56);
// gfx->getTextBounds("Chào thế giới!", 1, 56, &x1, &y1, &w, &h);
// gfx->drawRect(x1 - 1, y1 - 1, w + 2, h + 2, RGB565_RED);
gfx->println("Chào thế giới!");

#ifdef U8G2_USE_LARGE_FONTS
gfx->setFont(u8g2_font_unifont_t_chinese2);
gfx->setTextColor(RGB565_CYAN);
gfx->setCursor(0, 76);
// gfx->getTextBounds("世界你好!" , 1, 76, &x1, &y1, &w, &h);
// gfx->drawRect(x1 - 1, y1 - 1, w + 2, h + 2, RGB565_RED);
gfx->println("世界你好!") ;

gfx->setFont(u8g2_font_unifont_t_japanese1);
gfx->setTextColor(RGB565_BLUE);
gfx->setCursor(0, 96);
// gfx->getTextBounds("こんにちは世界!" , 1, 96, &x1, &y1, &w, &h);
// gfx->drawRect(x1 - 1, y1 - 1, w + 2, h + 2, RGB565_RED);
gfx->println("こんにちは世界!") ;

gfx->setFont(u8g2_font_unifont_t_korean1);
gfx->setTextColor(RGB565_MAGENTA);
gfx->setCursor(0, 116);
// gfx->getTextBounds("안녕하세요, 세계입니다!", 1, 116, &x1, &y1, &w, &h);
// gfx->drawRect(x1 - 1, y1 - 1, w + 2, h + 2, RGB565_RED);
gfx->println("안녕하세요, 세계입니다!");
#endif // U8G2_USE_LARGE_FONTS
}

void loop()
{
}

Operation Result

  • The screen displays as follows:

07_sd_card_test

This demo tests the TF card read/write functionality on the ESP32-S3-LCD-0.85.

Code

07_sd_card_test.ino
#include "FS.h"
#include "SD_MMC.h"

int clk = 16;
int cmd = 15;
int d0 = 17;
int d1 = 18;
int d2 = 13;
int d3 = 14;

void listDir(fs::FS &fs, const char *dirname, uint8_t levels) {
Serial.printf("Listing directory: %s\n", dirname);

File root = fs.open(dirname);
if (!root) {
Serial.println("Failed to open directory");
return;
}
if (!root.isDirectory()) {
Serial.println("Not a directory");
return;
}

File file = root.openNextFile();
while (file) {
if (file.isDirectory()) {
Serial.print(" DIR : ");
Serial.println(file.name());
if (levels) {
listDir(fs, file.path(), levels - 1);
}
}else{
Serial.print(" FILE: ");
Serial.print(file.name());
Serial.print(" SIZE: ");
Serial.println(file.size());
}
file = root.openNextFile();
}
}

void createDir(fs::FS &fs, const char *path) {
Serial.printf("Creating Dir: %s\n", path);
if (fs.mkdir(path)) {
Serial.println("Dir created");
}else{
Serial.println("mkdir failed");
}
}

void removeDir(fs::FS &fs, const char *path) {
Serial.printf("Removing Dir: %s\n", path);
if (fs.rmdir(path)) {
Serial.println("Dir removed");
}else{
Serial.println("rmdir failed");
}
}

void readFile(fs::FS &fs, const char *path) {
Serial.printf("Reading file: %s\n", path);

File file = fs.open(path);
if (!file) {
Serial.println("Failed to open file for reading");
return;
}

Serial.print("Read from file: ");
while (file.available()) {
Serial.write(file.read());
}
}

void writeFile(fs::FS &fs, const char *path, const char *message) {
Serial.printf("Writing file: %s\n", path);

File file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
}else{
Serial.println("Write failed");
}
}

void appendFile(fs::FS &fs, const char *path, const char *message) {
Serial.printf("Appending to file: %s\n", path);

File file = fs.open(path, FILE_APPEND);
if (!file) {
Serial.println("Failed to open file for appending");
return;
}
if (file.print(message)) {
Serial.println("Message appended");
}else{
Serial.println("Append failed");
}
}

void renameFile(fs::FS &fs, const char *path1, const char *path2) {
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (fs.rename(path1, path2)) {
Serial.println("File renamed");
}else{
Serial.println("Rename failed");
}
}

deleteFile(fs::FS &fs, const char *path) {
Serial.printf("Deleting file: %s\n", path);
if (fs.remove(path)) {
Serial.println("File deleted");
}else{
Serial.println("Delete failed");
}
}

void testFileIO(fs::FS &fs, const char *path) {
File file = fs.open(path);
static uint8_t buf[512];
size_t len = 0;
uint32_t start = millis();
uint32_t end = start;
if (file) {
len = file.size();
size_t flen = len;
start = millis();
while (len) {
size_t toRead = len;
if (toRead > 512) {
toRead = 512;
}
file.read(buf, toRead);
len -= toRead;
}
end = millis() - start;
Serial.printf("%u bytes read for %lu ms\n", flen, end);
file.close();
}else{
Serial.println("Failed to open file for reading");
}

file = fs.open(path, FILE_WRITE);
if (!file) {
Serial.println("Failed to open file for writing");
return;
}

size_t i;
start = millis();
for (i = 0; i < 2048; i++) {
file.write(buf, 512);
}
end = millis() - start;
Serial.printf("%u bytes written for %lu ms\n", 2048 * 512, end);
file.close();
}

void setup() {
Serial.begin(115200);
delay(3000);
if (!SD_MMC.setPins(clk, cmd, d0, d1, d2, d3)) {
Serial.println("Pin change failed!");
return;
}
if (!SD_MMC.begin()) {
Serial.println("Card Mount Failed");
return;
}
uint8_t cardType = SD_MMC.cardType();

if (cardType == CARD_NONE) {
Serial.println("No SD_MMC card attached");
return;
}

Serial.print("SD_MMC Card Type: ");
if (cardType == CARD_MMC) {
Serial.println("MMC");
} else if (cardType == CARD_SD) {
Serial.println("SDSC");
} else if (cardType == CARD_SDHC) {
Serial.println("SDHC");
}else{
Serial.println("UNKNOWN");
}

uint64_t cardSize = SD_MMC.cardSize() / (1024 * 1024);
Serial.printf("SD_MMC Card Size: %lluMB\n", cardSize);

listDir(SD_MMC, "/", 0);
createDir(SD_MMC, "/mydir");
listDir(SD_MMC, "/", 0);
removeDir(SD_MMC, "/mydir");
listDir(SD_MMC, "/", 2);
writeFile(SD_MMC, "/hello.txt", "Hello ");
appendFile(SD_MMC, "/hello.txt", "World!\n");
readFile(SD_MMC, "/hello.txt");
deleteFile(SD_MMC, "/foo.txt");
renameFile(SD_MMC, "/hello.txt", "/foo.txt");
readFile(SD_MMC, "/foo.txt");
testFileIO(SD_MMC, "/test.txt");
Serial.printf("Total space: %lluMB\n", SD_MMC.totalBytes() / (1024 * 1024));
Serial.printf("Used space: %lluMB\n", SD_MMC.usedBytes() / (1024 * 1024));
}

void loop() {
delay(10);
}

Code Analysis

  • TF card initialization:

    if (!SD_MMC.setPins(clk, cmd, d0, d1, d2, d3)) {
    Serial.println("Pin change failed!");
    return;
    }
    if (!SD_MMC.begin()) {
    Serial.println("Card Mount Failed");
    return;
    }

Operation Result

  • The screen shows no output
  • Open the serial monitor

08_lvgl_example_v8

This demo demonstrates how to run the lvgl demos (v8.4.0) on the ESP32-S3-LCD-0.85.

  • It is required to install lvgl v8.4.0 version. If you have installed other version, please reinstall.

Code

08_lvgl_example_v8.ino
/*Using LVGL with Arduino requires some extra steps:
*Be sure to read the docs here: https://docs.lvgl.io/master/get-started/platforms/arduino.html */

#include <lvgl.h>

/*To use the built-in examples and demos of LVGL uncomment the includes below respectively.
*You also need to copy `lvgl/examples` to `lvgl/src/examples`. Similarly for the demos `lvgl/demos` to `lvgl/src/demos`.
Note that the `lv_examples` library is for LVGL v7 and you shouldn't install it for this version (since LVGL v8)
as the examples and demos are now part of the main LVGL library. */

// #include <examples/lv_examples.h>
// #include <demos/lv_demos.h>

// #define DIRECT_MODE // Uncomment to enable full frame buffer

#include <Arduino_GFX_Library.h>

#define GFX_BL 46 // default backlight pin, you may replace DF_GFX_BL to actual backlight pin

/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_ESP32SPI(45 /* DC */, 21 /* CS */, 38 /* SCK */, 39 /* MOSI */, GFX_NOT_DEFINED /* MISO */);

/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
Arduino_GFX *gfx = new Arduino_GC9107(bus, 40 /* RST */, 0 /* rotation */, true /* IPS */);

uint32_t screenWidth;
uint32_t screenHeight;
uint32_t bufSize;
lv_disp_draw_buf_t draw_buf;
lv_color_t *disp_draw_buf1;
lv_color_t *disp_draw_buf2;
lv_disp_drv_t disp_drv;

/* Display flushing */
void my_disp_flush(lv_disp_drv_t *disp_drv, const lv_area_t *area, lv_color_t *color_p) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);

#if (LV_COLOR_16_SWAP != 0)
gfx->draw16bitBeRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#else
gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)&color_p->full, w, h);
#endif

lv_disp_flush_ready(disp_drv);
}

void setup() {

Serial.begin(115200);
Serial.println("Arduino_GFX Hello World example");

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

gfx->fillScreen(RGB565_BLACK);

#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif

lv_init();

screenWidth = gfx->width();
screenHeight = gfx->height();

bufSize = screenWidth * 120;

disp_draw_buf1 = (lv_color_t *)heap_caps_malloc(bufSize * 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);

disp_draw_buf2 = (lv_color_t *)heap_caps_malloc(bufSize * 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_8BIT);

lv_disp_draw_buf_init(&draw_buf, disp_draw_buf1, disp_draw_buf2, bufSize);

/*Initialize the display*/
lv_disp_drv_init(&disp_drv);
/*Change the following line to your display resolution*/
disp_drv.hor_res = screenWidth;
disp_drv.ver_res = screenHeight;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;

lv_disp_drv_register(&disp_drv);

/*Initialize the (dummy) input device driver*/
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello Arduino!\n (V" GFX_STR(LVGL_VERSION_MAJOR) "." GFX_STR(LVGL_VERSION_MINOR) "." GFX_STR(LVGL_VERSION_PATCH) ")");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);

/* Option 3: Or try out a demo. Don't forget to enable the demos in lv_conf.h. E.g. LV_USE_DEMOS_WIDGETS*/
// lv_demo_widgets();
// lv_demo_benchmark();
// lv_demo_keypad_encoder();
// lv_demo_music();
// lv_demo_stress();

Serial.println("Setup done");
}

void loop() {
lv_timer_handler(); /* let the GUI do its work */
delay(1);
}

Operation Result

  • The screen displays as follows:

09_lvgl_example_v9

This demo demonstrates how to run the lvgl (v9.3.0) demos on the ESP32-S3-LCD-0.85.

  • It is required to install lvgl v9.3.0 version. If you have installed other version, please reinstall.

Code

09_lvgl_example_v9.ino
/*Using LVGL with Arduino requires some extra steps:
*Be sure to read the docs here: https://docs.lvgl.io/master/get-started/platforms/arduino.html */

#include <lvgl.h>

/*To use the built-in examples and demos of LVGL uncomment the includes below respectively.
*You also need to copy `lvgl/examples` to `lvgl/src/examples`. Similarly for the demos `lvgl/demos` to `lvgl/src/demos`.
*Note that the `lv_examples` library is for LVGL v7 and you shouldn't install it for this version (since LVGL v8)
*as the examples and demos are now part of the main LVGL library. */

// #include <examples/lv_examples.h>
// #include <demos/lv_demos.h>

// #define DIRECT_RENDER_MODE // Uncomment to enable full frame buffer

#include <Arduino_GFX_Library.h>

#define GFX_BL 46 // default backlight pin, you may replace DF_GFX_BL to actual backlight pin

/* More data bus class: https://github.com/moononournation/Arduino_GFX/wiki/Data-Bus-Class */
Arduino_DataBus *bus = new Arduino_ESP32SPI(45 /* DC */, 21 /* CS */, 38 /* SCK */, 39 /* MOSI */, GFX_NOT_DEFINED /* MISO */);

/* More display class: https://github.com/moononournation/Arduino_GFX/wiki/Display-Class */
Arduino_GFX *gfx = new Arduino_GC9107(bus, 40 /* RST */, 0 /* rotation */, true /* IPS */);

uint32_t screenWidth;
uint32_t screenHeight;
uint32_t bufSize;
lv_display_t *disp;
lv_color_t *disp_draw_buf;

#if LV_USE_LOG != 0
void my_print(lv_log_level_t level, const char *buf)
{
LV_UNUSED(level);
Serial.println(buf);
Serial.flush();
}
#endif

uint32_t millis_cb(void)
{
return millis();
}

/* LVGL calls it when a rendered image needs to copied to the display*/
void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
#ifndef DIRECT_RENDER_MODE
uint32_t w = lv_area_get_width(area);
uint32_t h = lv_area_get_height(area);

gfx->draw16bitRGBBitmap(area->x1, area->y1, (uint16_t *)px_map, w, h);
#endif // #ifndef DIRECT_RENDER_MODE

/*Call it to tell LVGL you are ready*/
lv_disp_flush_ready(disp);
}

void setup()
{
#ifdef DEV_DEVICE_INIT
DEV_DEVICE_INIT();
#endif

Serial.begin(115200);
// Serial.setDebugOutput(true);
// while(!Serial);
Serial.println("Arduino_GFX LVGL_Arduino_v9 example ");
String LVGL_Arduino = String('V') + lv_version_major() + "." + lv_version_minor() + "." + lv_version_patch();
Serial.println(LVGL_Arduino);

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

#ifdef GFX_BL
pinMode(GFX_BL, OUTPUT);
digitalWrite(GFX_BL, HIGH);
#endif

lv_init();

/*Set a tick source so that LVGL will know how much time elapsed. */
lv_tick_set_cb(millis_cb);

/* register print function for debugging */
#if LV_USE_LOG != 0
lv_log_register_print_cb(my_print);
#endif

screenWidth = gfx->width();
screenHeight = gfx->height();

#ifdef DIRECT_RENDER_MODE
bufSize = screenWidth * screenHeight;
#else
bufSize = screenWidth * 120;
#endif

disp_draw_buf = (lv_color_t *)heap_caps_malloc(bufSize * 2, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
if (!disp_draw_buf)
{
// remove MALLOC_CAP_INTERNAL flag try again
disp_draw_buf = (lv_color_t *)heap_caps_malloc(bufSize * 2, MALLOC_CAP_8BIT);
}

if (!disp_draw_buf)
{
Serial.println("LVGL disp_draw_buf allocate failed!");
}
else
{
disp = lv_display_create(screenWidth, screenHeight);
lv_display_set_flush_cb(disp, my_disp_flush);
#ifdef DIRECT_RENDER_MODE
lv_display_set_buffers(disp, disp_draw_buf, NULL, bufSize * 2, LV_DISPLAY_RENDER_MODE_DIRECT);
#else
lv_display_set_buffers(disp, disp_draw_buf, NULL, bufSize * 2, LV_DISPLAY_RENDER_MODE_PARTIAL);
#endif

/*Initialize the (dummy) input device driver*/
// lv_indev_t *indev = lv_indev_create();
// lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); /*Touchpad should have POINTER type*/
// lv_indev_set_read_cb(indev, my_touchpad_read);

/* Option 1: Create a simple label
* ---------------------
*/
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "Hello Arduino, \nI'm LVGL!(V" GFX_STR(LVGL_VERSION_MAJOR) "." GFX_STR(LVGL_VERSION_MINOR) "." GFX_STR(LVGL_VERSION_PATCH) ")");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);

/* Option 2: Try an example. See all the examples
* - Online: https://docs.lvgl.io/master/examples.html
* - Source codes: https://github.com/lvgl/lvgl/tree/master/examples
* ----------------------------------------------------------------
*/
// lv_example_btn_1();

/* Option 3: Or try out a demo. Don't forget to enable the demos in lv_conf.h. E.g. LV_USE_DEMOS_WIDGETS
* -------------------------------------------------------------------------------------------
*/
// lv_demo_widgets();
// lv_demo_benchmark();
// lv_demo_keypad_encoder();
// lv_demo_music();
// lv_demo_stress();
}

Serial.println("Setup done");
}

void loop()
{
lv_task_handler(); /* let the GUI do its work */
delay(5);
}

Operation Result

  • The screen displays as follows: