ESP32 MAX31855 thermocouple LCD minimal example using PlatformIO

Important: In newer versions of PlatformIO, most ESP32 boards will crash with this example unless you create a custom lv_conf.h config file specifying to use internal memory instead of PSRAM. See How to fix ESP32 LVGL crash / reboot in lv_tlsf_create() / lv_mem_init() for instructions how to fix that.

In our previous example Adafruit ST7735R display minimal LVGL example for PlatformIO we showed how to use LVGL to display a simple text on a ST7735R-based SPI display.

In this post, we’ll extend this example by

See Minimal ESP32 PlatformIO TFT display example using Adafruit ST7735 for the recommended hardware configuration for the display. The code supports two MAX31855s but only one is used for this example. The pinout for the MAX31855 is:

constexpr int8_t Pin_MAX31855_CLK = 21;
constexpr int8_t Pin_MAX31855_MISO = 5;
constexpr int8_t Pin_MAX31855_A_CS = 18;

Using an ESP32 with a 128x160px ST7735R display, this code achieves a ~5Hz update rate. Note that this example can easily be modified to work with other LCD controllers or displays.

#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_LvGL_Glue.h>
#include <Adafruit_MAX31855.h>
#include <string>
#include <sstream>

constexpr int8_t Pin_LCD_CS = 27;
constexpr int8_t Pin_LCD_DC = 23;
constexpr int8_t Pin_LCD_RST = 22;
constexpr int8_t Pin_LCD_SCLK = 14;
constexpr int8_t Pin_LCD_MISO = 12;
constexpr int8_t Pin_LCD_MOSI = 13;

constexpr int8_t Pin_MAX31855_CLK = 21;
constexpr int8_t Pin_MAX31855_MISO = 5;
// Two different Chip selects for two different MAX31855
constexpr int8_t Pin_MAX31855_A_CS = 18;
constexpr int8_t Pin_MAX31855_B_CS = 19;

SPIClass vspi(VSPI);
Adafruit_MAX31855 thermocouple1(Pin_MAX31855_A_CS, &vspi);
Adafruit_MAX31855 thermocouple2(Pin_MAX31855_B_CS, &vspi);

Adafruit_ST7735 lcd(Pin_LCD_CS, Pin_LCD_DC, Pin_LCD_MOSI, Pin_LCD_SCLK,
Adafruit_LvGL_Glue glue;

// Number of consecutive NaN reads before we assume the thermocouple is actually disconnected
uint32_t consecutiveNaNReads = 0;
constexpr uint32_t maxConsecutiveNaNReads = 10;
std::string labelText = "";
lv_obj_t *label;

void lvgl_setup(void) {
  // Create simple label centered on screen
  label = lv_label_create(lv_scr_act());
  lv_label_set_text(label, "Initializing..");
  lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);

template<typename T>
std::string celsiusToString(const T a_value, const int decimals = 1)
    std::ostringstream out;
    out << std::fixed << a_value << " °C";
    return std::move(out).str();

bool IsValidReading(double reading) {
  return !isnan(reading) && reading > -40.0 && reading < 1000.0;

void ReadTemperature() {
  double celsius = thermocouple1.readCelsius();
  if(!IsValidReading(celsius)) {
    if(consecutiveNaNReads >= maxConsecutiveNaNReads) {
      // Thermocouple is disconnected
      lv_label_set_text(label, "Error");
  } else {
    consecutiveNaNReads = 0;
    labelText = "T: " + celsiusToString(celsius);
    lv_label_set_text(label, labelText.c_str());

void setup() {
  // Start MAX31855 SPI
  vspi.begin(Pin_MAX31855_CLK, Pin_MAX31855_MISO, Pin_MAX31855_A_CS);

  lcd.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab

  LvGLStatus status = glue.begin(&lcd);
  if(status != LVGL_OK) {
    Serial.printf("Glue error %d\r\n", (int)status);

  lvgl_setup(); // Call UI-building function above

void loop() {
  lv_task_handler(); // Call LittleVGL task handler periodically
platform = espressif32
board = esp32dev
framework = arduino
monitor_speed = 115200
lib_deps =
    adafruit/Adafruit GFX Library@^1.11.5
    adafruit/Adafruit ST7735 and ST7789 Library@^1.10.0
    adafruit/Adafruit LittlevGL Glue Library@^2.1.4
    adafruit/SdFat - Adafruit Fork@^2.2.1
    adafruit/Adafruit MAX31855 library@^1.4.0