ESP32 S2 with ST7789 – SD card

By the time I have got this cleaned up I might have found a use for it !
Still another challenge for these powerhouse ESP32 microcontrollers.
On the back is a slot for SD card, and I’ll use this page to document the how and possibilities.
An amusing aside: The SD card slot is tightly slipped into the “underside” of the board. I might use pin sockets when I install these, since soldering the board into a project makes removal/ replacement of the SD card impossible!

As they say in the classics: “Read the docs”.
So here is my definitive IO reference for the these ESP32 S2 T8 boards:

General declarations:

/// Screen
#define TFT_CS      34
#define TFT_RST     38 
#define TFT_DC      37  
#define TFT_MOSI    35  
#define TFT_SCLK    36 

/// SD Card
#define HAS_SDCARD
#define SD_CS_PIN   10
#define SD_MOSI_PIN 11
#define SD_SCK_PIN  12 
#define SD_MISO_PIN 13

In void setup():

  pinMode(33, OUTPUT);
  digitalWrite(33, HIGH); // Turn on Backlight LED
  pinMode(14, OUTPUT);
  digitalWrite(14, HIGH); // Turn on SD card for battery use

Combining Screen and SD Card access

This example shows both working together.
The functions are built from the common libraries for SD Card.
Refer to these for further file handling functions.
Here, we:

  • Access the SDcard
  • Delete a file
  • Create a file, put text in it.
  • Read from the card
  • Display the text from file
  • Display a directory listing
  • Get some stats from SDcard
Open, Read file, List.
/*
  Example using Screen and SD Card together 
  Note turn on GPIO 14 high, needed to support battery operation.
*/
//Screen
#define TFT_CS      34
#define TFT_RST     38
#define TFT_DC      37
#define TFT_MOSI    35
#define TFT_SCLK    36
//SD Card
#define HAS_SDCARD
#define SD_CS_PIN 10
#define SD_MOSI_PIN 11
#define SD_SCK_PIN 12  //CLK
#define SD_MISO_PIN 13

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include "FS.h"
#include "SD.h"
#include "SPI.h"
SPIClass spi = SPIClass(HSPI);
Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

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;
  }
  tft.setCursor(0, 60);
  tft.setTextSize(2);
  tft.setTextColor(ST77XX_YELLOW);
  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());
      tft.print(String(file.name()) + " / ");
    }
    file = root.openNextFile();
  }
}

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: ");
  tft.setCursor(0, 30);
  tft.setTextColor(ST77XX_GREEN);
  while (file.available()) {
    char readByte = file.read();
    //    Serial.write(file.read());
    Serial.print(readByte);
    tft.print(readByte);
  }
  file.close();
}

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");
  }
  file.close();
}

void 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 setup() {
  Serial.begin(115200);
  pinMode(33, OUTPUT);
  digitalWrite(33, HIGH); // Turn on LED
  pinMode(14, OUTPUT);
  digitalWrite(14, HIGH); // Turn on SD card for battery use
  tft.init(135, 240);
  tft.setRotation(3);
  spi.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);
  tft.fillScreen(ST77XX_BLACK);
  tft.setTextSize(3);
  tft.setCursor(0, 0);
  tft.setTextColor(ST77XX_WHITE);
  tft.print("ESP32 SD card");

  if (!SD.begin(SD_CS_PIN, spi, 80000000)) {
    Serial.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD.cardType();

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

  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);
  deleteFile(SD, "/hello.txt");
  writeFile(SD, "/hello.txt", "This is cool");
  listDir(SD, "/", 0);
  readFile(SD, "/hello.txt");
  Serial.printf("Total space: %lluMB\n" + SD.totalBytes() / (1024 * 1024));
  Serial.printf("Used space: %lluMB\n" + SD.usedBytes() / (1024 * 1024));

}

void loop() {

}

STOP PRESS: The “official” page for this microcontroller points out the need to set GPIO14 High to allow battery operation.
(Found that out the hard way!)

More to follow…

4 comments

    1. Don’t need one! It’s all on the Board:
      https://www.banggood.com/LILYGO-TTGO-T8-ESP32-S2-V1_1-ST7789-1_14-Inch-LCD-Display-WIFI-Wireless-Module-Type-c-Connector-TF-Card-Slot-Development-Board-p-1862731.html?cur_warehouse=CN
      … Unfortunately they have been out of stock for a while. I hope that they return.
      Technically the same result wiring up the 4 SPI connecters from an SDcard module to another ESP32 with screen.
      I’ll be trying this with the more popular, but lower spec board that lacks SDcard.
      https://www.banggood.com/LILYGO-TTGO-T-Display-ESP32-CH9102F-WiFi-bluetooth-Module-1_14-Inch-LCD-Development-Board-p-1869556.html?rmmds=detail-left-hotproducts&cur_warehouse=CN&ID=6309998&trace_id=60651643493933762
      Good luck.

  1. Hi, thanks for sharing.
    I’m curious about the manufacturer design decision to split the SPI bus for the display and SD card. Maybe you have an idea why? I’m trying to design my custom board and looking for a reference, got this but I think it taking unnecessary IO on the 2nd SPI bus while it can just be paralleled with a separate chip select pin. What do you think?

Leave a comment