Xiao ESP32-S3 and ILI9341 Adafruit Library Working!

I finally got the Adafruit ili9341 library working with the Xiao ESP32-S3!

I was reading a rather heated conversation between PJ Glasso and Energia about pin assignments on various ESP32 boards, when the magnitude of my stupidity hit me. In my Arduino sketch I had been using D pin references and not GPIO pin references, so it wasn’t surprising my sketch wasn’t working!

I now have the standard DEADBEEF application running on a ili 9341 TFT display with the Xiao esp32-s3:

Here it is below.

Pin assignments in code are the ESP32-S3 GPIO. Nothing else works…

<#include <SPI.h>
#include “Adafruit_GFX.h”
#include “Adafruit_HX8357.h”

// Analog meter display demo on HX8357 3.5" TFT
// Adapted from
/*
An example analogue meter using a ILI9341 TFT LCD screen
Alan Senior 23/2/2015
*/

// For the Adafruit shield, these are the default.
#define TFT_CS 44
#define TFT_DC 43
#define TFT_RST 6

#define TFT_CLK 7
#define TFT_MISO 8
#define TFT_MOSI 9

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);

// analog and digital meter globals
float ltx = 0; // Saved x coord of bottom of needle
int16_t osx = 120, osy = 120; // Saved x & y coords
uint32_t updateTime = 0; // time for next update
int old_analog = -999; // Value last displayed
int old_digital = -999; // Value last displayed

void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println(“HX8357D Analog Meter!”);

tft.begin();

//check display bounds
int h = tft.height();
int w = tft.width();
char line[40];
snprintf(line, sizeof line, “default h %d w %d”, h, w);
Serial.println(line);

// initialize screen
tft.setRotation(1); // horizontal wide screen
h = tft.height(); //check if properly reassigned
w = tft.width();
tft.fillScreen( HX8357_BLACK );
tft.setCursor(0, 2);
tft.setTextColor(HX8357_GREEN);
tft.setTextSize(2);
snprintf(line, sizeof line, “Rot: 1 h %d w %d”, h, w);
tft.print(line);
delay(1000);
analogMeter();
// digitalMeter(); // Draw digital meter, needs work
}

void loop() {
if (updateTime <= millis()) {
updateTime = millis() + 500;
int reading = random(-50, 151); // Test with random values
plotNeedle(reading, 8); // Update analogue meter, 8ms delay per needle increment
// digital meter does display numbers, but needs work
// showDigital(reading); // Update digital reading
}
}

// #########################################################################
// Draw the analogue meter on the screen
// #########################################################################
void analogMeter()
{
// Meter outline
#define HX8357_GREY 0x5AEB
tft.fillRect(0, 0, 259, 126, HX8357_GREY); //239, 230
tft.fillRect(5, 3, 250, 119, HX8357_WHITE);

tft.setTextColor(HX8357_BLACK); // Text colour

// Draw ticks every 5 degrees from -50 to +50 degrees (100 deg. FSD swing)
for (int i = -50; i < 51; i += 5) {
// Long scale tick length
int tl = 15;

float dtr = PI / 180.0;

// Coodinates of tick to draw
float sx = cos((i - 90) * dtr);
float sy = sin((i - 90) * dtr);
int16_t x0 = sx * (100 + tl) + 120;
int16_t y0 = sy * (100 + tl) + 140;
int16_t x1 = sx * 100 + 120;
int16_t y1 = sy * 100 + 140;

// Coordinates of next tick for zone fill
float sx2 = cos((i + 5 - 90) * dtr);
float sy2 = sin((i + 5 - 90) * dtr);
int x2 = sx2 * (100 + tl) + 120;
int y2 = sy2 * (100 + tl) + 140;
int x3 = sx2 * 100 + 120;
int y3 = sy2 * 100 + 140;

// Green zone limits
if (i >= 0 && i < 25) {  //center to 3/4
  tft.fillTriangle(x0, y0, x1, y1, x2, y2, HX8357_GREEN);
  tft.fillTriangle(x1, y1, x2, y2, x3, y3, HX8357_GREEN);
}

// Yellow zone limits
if (i >= 25 && i < 50) { //3/4 to full
  tft.fillTriangle(x0, y0, x1, y1, x2, y2, HX8357_YELLOW);
  tft.fillTriangle(x1, y1, x2, y2, x3, y3, HX8357_YELLOW);
}

// Short scale tick length
if (i % 25 != 0) tl = 8;

// Recalculate coords incase tick lenght changed
x0 = sx * (100 + tl) + 120;
y0 = sy * (100 + tl) + 140;
x1 = sx * 100 + 120;
y1 = sy * 100 + 140;

// Draw tick
tft.drawLine(x0, y0, x1, y1, HX8357_BLACK);

// Check if labels should be drawn, with position tweaks
if (i % 25 == 0) {
  // Calculate label positions
  x0 = sx * (100 + tl + 10) + 120;
  y0 = sy * (100 + tl + 10) + 140;
  tft.setTextSize(2);
  switch (i / 25) {
    case -2: tft.setCursor(x0, y0 - 12); tft.print("0"); break;
    case -1: tft.setCursor(x0, y0 - 9); tft.print("25"); break;
    case 0: tft.setCursor(x0, y0 - 6); tft.print("50"); break;
    case 1: tft.setCursor(x0, y0 - 9); tft.print("75"); break;
    case 2: tft.setCursor(x0 - 5, y0 - 12); tft.print("100"); break;
  }
}

// Now draw the arc of the scale
sx = cos((i + 5 - 90) * dtr);
sy = sin((i + 5 - 90) * dtr);
x0 = sx * 100 + 120;
y0 = sy * 100 + 140;
// Draw scale arc, don't draw the last part
if (i < 50) tft.drawLine(x0, y0, x1, y1, HX8357_BLACK);

}

// tft.drawString(“%RH”, 5 + 230 - 40, 119 - 20, 2); // Units at bottom right
// tft.drawCentreString(“%RH”, 120, 70, 4); // Comment out to avoid font 4
tft.drawRect(5, 3, 250, 119, HX8357_BLACK); // Draw bezel line (230)

plotNeedle(0, 0); // Put meter needle at 0
}
// #########################################################################
// Update needle position
// This function is blocking while needle moves, time depends on ms_delay
// 10ms minimises needle flicker if text is drawn within needle sweep area
// Smaller values OK if text not in sweep area, zero for instant movement but
// does not look realistic… (note: 100 increments for full scale deflection)
// #########################################################################
void plotNeedle(int value, byte ms_delay)
{
float dtr = PI / 180.0;
tft.setTextColor(HX8357_BLACK, HX8357_WHITE);

if (value < -10) value = -10; // Limit value to emulate needle end stops
if (value > 110) value = 110;

// Move the needle util new value reached
while (!(value == old_analog)) {
if (old_analog < value) old_analog++;
else old_analog–;

if (ms_delay == 0) old_analog = value; // Update immediately id delay is 0

float sdeg = map(old_analog, -10, 110, -150, -30); // Map value to angle
// Calculate tip of needle coords
float sx = cos(sdeg * dtr);
float sy = sin(sdeg * dtr);

// Calculate x delta of needle start (does not start at pivot point)
float tx = tan((sdeg + 90) * dtr);

// Erase old needle image
tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, HX8357_WHITE);
tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, HX8357_WHITE);
tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, HX8357_WHITE);

// Store new needle end coords for next erase
ltx = tx;
osx = sx * 98 + 120;
osy = sy * 98 + 140;

// Draw the needle in the new postion, magenta makes needle a bit bolder
// draws 3 lines to thicken needle
tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, HX8357_RED);
tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, HX8357_MAGENTA);
tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, HX8357_RED);

// Slow needle down slightly as it approaches new postion
if (abs(old_analog - value) < 10) ms_delay += ms_delay / 5;

// Wait before next update
delay(ms_delay);

}
}

/* the code below compiles and works, but meter frame constants need updating for new character scales
// #########################################################################
// Draw 3 digit digital meter with faint 7 segment image
// #########################################################################
void digitalMeter()
{
int xpos = 118, ypos = 134; // was 134
tft.fillRect(xpos - 52, ypos - 5, 2 * 54, 59, HX8357_GREY);
tft.fillRect(xpos - 49, ypos - 2, 2 * 51, 53, HX8357_BLACK);
tft.setTextColor(7 << 11, HX8357_BLACK); // Plot over numbers in dim red
tft.setCursor(xpos - 48, ypos + 1); tft.setTextSize(6); tft.print(“888”);
}

// #########################################################################
// Update digital meter reading
// #########################################################################
void showDigital(int value)
{
if (value == old_digital) return; // return if no change to prevent flicker
if (value < 0) value = 0; //Constrain lower limit to 0
if (value > 999) value = 999; //Constrain upper limit to 999

int xpos = 118, ypos = 134 + 1; // Position with location tweak
tft.setTextColor(7 << 11, HX8357_BLACK); // Plot over numbers in dim red
tft.setCursor(xpos - 47, ypos); tft.setTextSize(6); tft.print(“888”); //Erase old value

// Nb. 32 pixels wide +2 gap per digit

// Update with new value
tft.setTextColor(HX8357_RED); // Dont draw background to leave dim segments
if (value < 10) {
tft.setCursor(xpos + 19, ypos);
tft.setTextSize(6);
tft.print(value);
}
else if (value < 100) {
tft.setCursor(xpos - 14, ypos);
tft.setTextSize(6);
tft.print(value);
}
else {
tft.setCursor(xpos - 47, ypos);
tft.setTextSize(6);
tft.print(value);
}
old_digital = value;
}
*/
/>

1 Like

Hi there,

LOL, yep for sure… Looks freaking GOOD too!
Nice work staying with it and READING everything.

Sometimes it’s the discussion that leads the way :+1: it’s why we are all here and WE all have something to offer hopefully.

And Yes the GPIO’s in place of the Logical Pin Names will save the bacon on older code snips too. :grin:

I don’t know if it’s marked as a SOLUTION but check and do us a solid and mark it so, that way others can find it fast.

Great work , Glad you got it going :+1:

HTH
GL :slight_smile: PJ :v:

I used the code tags above “</>” pasted it in there. Looks easier to read,:v:

<#include <SPI.h>
#include “Adafruit_GFX.h”
#include “Adafruit_HX8357.h”

// Analog meter display demo on HX8357 3.5" TFT
// Adapted from
/*
An example analogue meter using a ILI9341 TFT LCD screen
Alan Senior 23/2/2015
*/

// For the Adafruit shield, these are the default.
#define TFT_CS 44
#define TFT_DC 43
#define TFT_RST 6

#define TFT_CLK 7
#define TFT_MISO 8
#define TFT_MOSI 9

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);

// analog and digital meter globals
float ltx = 0; // Saved x coord of bottom of needle
int16_t osx = 120, osy = 120; // Saved x & y coords
uint32_t updateTime = 0; // time for next update
int old_analog = -999; // Value last displayed
int old_digital = -999; // Value last displayed

void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println(“HX8357D Analog Meter!”);

tft.begin();

//check display bounds
int h = tft.height();
int w = tft.width();
char line[40];
snprintf(line, sizeof line, “default h %d w %d”, h, w);
Serial.println(line);

// initialize screen
tft.setRotation(1); // horizontal wide screen
h = tft.height(); //check if properly reassigned
w = tft.width();
tft.fillScreen( HX8357_BLACK );
tft.setCursor(0, 2);
tft.setTextColor(HX8357_GREEN);
tft.setTextSize(2);
snprintf(line, sizeof line, “Rot: 1 h %d w %d”, h, w);
tft.print(line);
delay(1000);
analogMeter();
// digitalMeter(); // Draw digital meter, needs work
}

void loop() {
if (updateTime <= millis()) {
updateTime = millis() + 500;
int reading = random(-50, 151); // Test with random values
plotNeedle(reading, 8); // Update analogue meter, 8ms delay per needle increment
// digital meter does display numbers, but needs work
// showDigital(reading); // Update digital reading
}
}

// #########################################################################
// Draw the analogue meter on the screen
// #########################################################################
void analogMeter()
{
// Meter outline
#define HX8357_GREY 0x5AEB
tft.fillRect(0, 0, 259, 126, HX8357_GREY); //239, 230
tft.fillRect(5, 3, 250, 119, HX8357_WHITE);

tft.setTextColor(HX8357_BLACK); // Text colour

// Draw ticks every 5 degrees from -50 to +50 degrees (100 deg. FSD swing)
for (int i = -50; i < 51; i += 5) {
// Long scale tick length
int tl = 15;

float dtr = PI / 180.0;

// Coodinates of tick to draw
float sx = cos((i - 90) * dtr);
float sy = sin((i - 90) * dtr);
int16_t x0 = sx * (100 + tl) + 120;
int16_t y0 = sy * (100 + tl) + 140;
int16_t x1 = sx * 100 + 120;
int16_t y1 = sy * 100 + 140;

// Coordinates of next tick for zone fill
float sx2 = cos((i + 5 - 90) * dtr);
float sy2 = sin((i + 5 - 90) * dtr);
int x2 = sx2 * (100 + tl) + 120;
int y2 = sy2 * (100 + tl) + 140;
int x3 = sx2 * 100 + 120;
int y3 = sy2 * 100 + 140;

// Green zone limits
if (i >= 0 && i < 25) {  //center to 3/4
  tft.fillTriangle(x0, y0, x1, y1, x2, y2, HX8357_GREEN);
  tft.fillTriangle(x1, y1, x2, y2, x3, y3, HX8357_GREEN);
}

// Yellow zone limits
if (i >= 25 && i < 50) { //3/4 to full
  tft.fillTriangle(x0, y0, x1, y1, x2, y2, HX8357_YELLOW);
  tft.fillTriangle(x1, y1, x2, y2, x3, y3, HX8357_YELLOW);
}

// Short scale tick length
if (i % 25 != 0) tl = 8;

// Recalculate coords incase tick lenght changed
x0 = sx * (100 + tl) + 120;
y0 = sy * (100 + tl) + 140;
x1 = sx * 100 + 120;
y1 = sy * 100 + 140;

// Draw tick
tft.drawLine(x0, y0, x1, y1, HX8357_BLACK);

// Check if labels should be drawn, with position tweaks
if (i % 25 == 0) {
  // Calculate label positions
  x0 = sx * (100 + tl + 10) + 120;
  y0 = sy * (100 + tl + 10) + 140;
  tft.setTextSize(2);
  switch (i / 25) {
    case -2: tft.setCursor(x0, y0 - 12); tft.print("0"); break;
    case -1: tft.setCursor(x0, y0 - 9); tft.print("25"); break;
    case 0: tft.setCursor(x0, y0 - 6); tft.print("50"); break;
    case 1: tft.setCursor(x0, y0 - 9); tft.print("75"); break;
    case 2: tft.setCursor(x0 - 5, y0 - 12); tft.print("100"); break;
  }
}

// Now draw the arc of the scale
sx = cos((i + 5 - 90) * dtr);
sy = sin((i + 5 - 90) * dtr);
x0 = sx * 100 + 120;
y0 = sy * 100 + 140;
// Draw scale arc, don't draw the last part
if (i < 50) tft.drawLine(x0, y0, x1, y1, HX8357_BLACK);
}

// tft.drawString(“%RH”, 5 + 230 - 40, 119 - 20, 2); // Units at bottom right
// tft.drawCentreString(“%RH”, 120, 70, 4); // Comment out to avoid font 4
tft.drawRect(5, 3, 250, 119, HX8357_BLACK); // Draw bezel line (230)

plotNeedle(0, 0); // Put meter needle at 0
}
// #########################################################################
// Update needle position
// This function is blocking while needle moves, time depends on ms_delay
// 10ms minimises needle flicker if text is drawn within needle sweep area
// Smaller values OK if text not in sweep area, zero for instant movement but
// does not look realistic… (note: 100 increments for full scale deflection)
// #########################################################################
void plotNeedle(int value, byte ms_delay)
{
float dtr = PI / 180.0;
tft.setTextColor(HX8357_BLACK, HX8357_WHITE);

if (value < -10) value = -10; // Limit value to emulate needle end stops
if (value > 110) value = 110;

// Move the needle util new value reached
while (!(value == old_analog)) {
if (old_analog < value) old_analog++;
else old_analog–;

if (ms_delay == 0) old_analog = value; // Update immediately id delay is 0

float sdeg = map(old_analog, -10, 110, -150, -30); // Map value to angle
// Calculate tip of needle coords
float sx = cos(sdeg * dtr);
float sy = sin(sdeg * dtr);

// Calculate x delta of needle start (does not start at pivot point)
float tx = tan((sdeg + 90) * dtr);

// Erase old needle image
tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, HX8357_WHITE);
tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, HX8357_WHITE);
tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, HX8357_WHITE);

// Store new needle end coords for next erase
ltx = tx;
osx = sx * 98 + 120;
osy = sy * 98 + 140;

// Draw the needle in the new postion, magenta makes needle a bit bolder
// draws 3 lines to thicken needle
tft.drawLine(120 + 20 * ltx - 1, 140 - 20, osx - 1, osy, HX8357_RED);
tft.drawLine(120 + 20 * ltx, 140 - 20, osx, osy, HX8357_MAGENTA);
tft.drawLine(120 + 20 * ltx + 1, 140 - 20, osx + 1, osy, HX8357_RED);

// Slow needle down slightly as it approaches new postion
if (abs(old_analog - value) < 10) ms_delay += ms_delay / 5;

// Wait before next update
delay(ms_delay);
}
}

/* the code below compiles and works, but meter frame constants need updating for new character scales
// #########################################################################
// Draw 3 digit digital meter with faint 7 segment image
// #########################################################################
void digitalMeter()
{
int xpos = 118, ypos = 134; // was 134
tft.fillRect(xpos - 52, ypos - 5, 2 * 54, 59, HX8357_GREY);
tft.fillRect(xpos - 49, ypos - 2, 2 * 51, 53, HX8357_BLACK);
tft.setTextColor(7 << 11, HX8357_BLACK); // Plot over numbers in dim red
tft.setCursor(xpos - 48, ypos + 1); tft.setTextSize(6); tft.print(“888”);
}

// #########################################################################
// Update digital meter reading
// #########################################################################
void showDigital(int value)
{
if (value == old_digital) return; // return if no change to prevent flicker
if (value < 0) value = 0; //Constrain lower limit to 0
if (value > 999) value = 999; //Constrain upper limit to 999

int xpos = 118, ypos = 134 + 1; // Position with location tweak
tft.setTextColor(7 << 11, HX8357_BLACK); // Plot over numbers in dim red
tft.setCursor(xpos - 47, ypos); tft.setTextSize(6); tft.print(“888”); //Erase old value

// Nb. 32 pixels wide +2 gap per digit

// Update with new value
tft.setTextColor(HX8357_RED); // Dont draw background to leave dim segments
if (value < 10) {
tft.setCursor(xpos + 19, ypos);
tft.setTextSize(6);
tft.print(value);
}
else if (value < 100) {
tft.setCursor(xpos - 14, ypos);
tft.setTextSize(6);
tft.print(value);
}
else {
tft.setCursor(xpos - 47, ypos);
tft.setTextSize(6);
tft.print(value);
}
old_digital = value;
}