R. Scott Coppersmith
Published © Apache-2.0

Arduino Uno Q and TFT Display

The Arduino Uno Q is a very powerful and feature rich device, however, some features are tricky to implement like TFT displays, SD files.

IntermediateProtip4 hours63
Arduino Uno Q and TFT Display

Things used in this project

Hardware components

Arduino Uno Q
Arduino Uno Q
×1
DIYables 3.5in TFT Display Shield for Arduino
×1

Story

Read more

Code

Arduino Uno Q with DIYables 3.5 inch 480 x 240 TFT display

C/C++
Arduino Uno Q with DIYables 3.5 inch 480 x 240 TFT display
/*
   Arduino Uno Q with DIYables 3.5 inch 480 x 240 TFT display
   CapperLabs 11 January 2026
   *** To Do ***
   
*/

#include <Arduino.h>
#include <Arduino_RouterBridge.h>
#include <Adafruit_GFX.h>    // Core graphics library
#include <DIYables_TFT_Shield.h>
#include <SD.h>
#include <Fonts/FreeSansBold24pt7b.h>

#define MAGENTA   DIYables_TFT::colorRGB(255, 0, 255)
#define ORANGE    DIYables_TFT::colorRGB(255, 165, 0)
#define PINK      DIYables_TFT::colorRGB(255, 192, 203)
#define VIOLET    DIYables_TFT::colorRGB(148, 0, 211)
#define TURQUOISE DIYables_TFT::colorRGB(64, 224, 208)
#define YELLOW    DIYables_TFT::colorRGB(255, 255, 40)
#define BLACK   0x0000
#define RED     0xF800
#define BLUE    0x001F
#define GREEN   0x07E0
#define WHITE   0xFFFF
#define GREY    0x8410

#define BUFFPIXEL 20  // Buffer size remains the same

DIYables_TFT_ILI9486_Shield TFT_display;
#define SD_CS 10
File bmpFile;
uint16_t SCREEN_WIDTH;
uint16_t SCREEN_HEIGHT;

void showmsgXY(int x, int y, int sz, const GFXfont *f, const char *msg)
{
    int16_t x1, y1;
    uint16_t wid, ht;
    TFT_display.setFont(f);
    TFT_display.setCursor(x, y);
    TFT_display.setTextColor(GREEN);
    TFT_display.setTextSize(sz);
    TFT_display.print(msg);
}
  // Create a Bridge instance

void setup() {

  Bridge.begin();
  Monitor.begin();
  if (!SD.begin(SD_CS)) {
    Monitor.println("SD card initialization failed!");
    return;
  }
  Monitor.println("SD card initialized successfully.");
  Monitor.println(F("TFT Test Jan2026"));

  TFT_display.begin();
  TFT_display.setFont(&FreeSansBold24pt7b);
  // Set the rotation (0 to 3)
  TFT_display.setRotation(1);  // Rotate screen 90 degrees
  // After rotation, update screen dimensions
  SCREEN_WIDTH = TFT_display.width();
  SCREEN_HEIGHT = TFT_display.height();
  TFT_display.fillScreen(BLACK);
  // Outlined round rectangle (Full Screen)
  TFT_display.drawRoundRect(5, 5, 470, 310, 15, RED);
  showmsgXY(130, 120, 1, &FreeSansBold24pt7b, "TFT Test");
  showmsgXY(130, 200, 1, &FreeSansBold24pt7b, "Jan 2026");
  // Show a .bmp file from SD card
  // Get image dimensions
  uint32_t imgWidth, imgHeight;
  if (getBMPDimensions("mybmp.bmp", imgWidth, imgHeight)) {
    Monitor.print("BMP Image Width: ");
    Monitor.println(imgWidth);
    Monitor.print("BMP Image Height: ");
    Monitor.println(imgHeight);
  } else {
    Monitor.println("Failed to get BMP dimensions");
  }

  // Center the image based on its dimensions
  int x = (SCREEN_WIDTH - imgWidth) / 2;
  int y = (SCREEN_HEIGHT - imgHeight) / 2;

  drawBMP("mybmpf.bmp", x, 220);  // Draw image at calculated position
  delay(2000);
}
void loop(void) {
  delay(2000);
  TFT_display.fillScreen(BLACK);
  TFT_display.drawRoundRect(5, 5, 470, 310, 15, RED);
  showmsgXY(130, 120, 1, &FreeSansBold24pt7b, "TFT Test");
  showmsgXY(170, 200, 1, &FreeSansBold24pt7b, "Jan 2026");
  drawBMP("mybmp.bmp", 120, 220);  // Draw image at calculated position
  delay(2000);
}
// Helper functions to read BMP file header
uint16_t read16(File &f) {
  uint16_t result;
  result = f.read();
  result |= (f.read() << 8);
  return result;
}

uint32_t read32(File &f) {
  uint32_t result;
  result = f.read();
  result |= ((uint32_t)f.read() << 8);
  result |= ((uint32_t)f.read() << 16);
  result |= ((uint32_t)f.read() << 24);
  return result;
}

// Function to read a signed 32-bit integer
int32_t readS32(File &f) {
  int32_t result;
  result = f.read();
  result |= ((uint32_t)f.read() << 8);
  result |= ((uint32_t)f.read() << 16);
  result |= ((uint32_t)f.read() << 24);
  return result;
}

// Function to draw BMP from SD card
void drawBMP(const char *filename, int x, int y) {
  bmpFile = SD.open(filename);
  if (!bmpFile) {
    Monitor.println("File not found");
    return;
  }

  if (read16(bmpFile) != 0x4D42) {  // Check BMP signature
    Monitor.println("Not a BMP file");
    bmpFile.close();
    return;
  }

  // Skip unnecessary BMP header details
  Monitor.println("BMP signature OK");

  uint32_t fileSize = read32(bmpFile);
  Monitor.print("File Size: ");
  Monitor.println(fileSize);

  read32(bmpFile);                         // Reserved bytes (skip)
  uint32_t imageOffset = read32(bmpFile);  // Start of image data
  Monitor.print("Image Data Offset: ");
  Monitor.println(imageOffset);

  uint32_t dibHeaderSize = read32(bmpFile);  // DIB header size
  Monitor.print("DIB Header Size: ");
  Monitor.println(dibHeaderSize);

  // Now read the width and height of the image
  uint32_t bmpWidth = read32(bmpFile);
  int32_t bmpHeight = readS32(bmpFile);  // Read as signed 32-bit integer
  Monitor.print("Image Width: ");
  Monitor.println(bmpWidth);
  Monitor.print("Image Height: ");
  Monitor.println(bmpHeight);

  bool topDown = true;  // Flag to check if the image is top-down
  if (bmpHeight < 0) {
    bmpHeight = -bmpHeight;  // Make height positive for processing
    topDown = true;          // Mark the BMP as top-down
  }

  if (read16(bmpFile) != 1) {  // Planes must be 1
    Monitor.println("Invalid BMP file");
    bmpFile.close();
    return;
  }

  uint16_t depth = read16(bmpFile);  // Color depth
  Monitor.print("Bit Depth: ");
  Monitor.println(depth);

  if (depth != 24) {  // Only 24-bit BMP supported
    Monitor.println("Only 24-bit BMP is supported");
    bmpFile.close();
    return;
  }

  if (read32(bmpFile) != 0) {  // No compression
    Monitor.println("Unsupported BMP compression");
    bmpFile.close();
    return;
  }

  // Move to the start of the image data
  bmpFile.seek(imageOffset);

  uint8_t sdbuffer[3 * BUFFPIXEL];  // Buffer for 20 pixels (3 bytes per pixel)
  uint16_t color;
  uint32_t rowSize = (bmpWidth * 3 + 3) & ~3;  // BMP rows are padded to 4-byte boundaries

  // Adjust x and y if image is larger than screen
  if (x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) {
    Monitor.println("Image position out of screen bounds");
    return;
  }

  uint32_t maxRow = min(bmpHeight, SCREEN_HEIGHT - y);
  uint32_t maxCol = min(bmpWidth, SCREEN_WIDTH - x);

  // Draw the image
  for (uint32_t row = 0; row < maxRow; row++) {
    int32_t rowPos = topDown ? row : bmpHeight - 1 - row;  // Adjust for top-down BMPs

    uint32_t filePosition = imageOffset + rowPos * rowSize;
    bmpFile.seek(filePosition);  // Move to the correct row

    for (uint32_t col = 0; col < maxCol; col += BUFFPIXEL) {
      uint32_t pixelsToRead = min(BUFFPIXEL, maxCol - col);  // Avoid reading beyond row width
      bmpFile.read(sdbuffer, 3 * pixelsToRead);              // Read multiple pixels at once

      for (uint32_t i = 0; i < pixelsToRead; i++) {
        uint8_t b = sdbuffer[i * 3];
        uint8_t g = sdbuffer[i * 3 + 1];
        uint8_t r = sdbuffer[i * 3 + 2];
        color = DIYables_TFT::colorRGB(r, g, b);

        // Draw pixel on screen if within bounds
        if ((x + col + i) < SCREEN_WIDTH && (y + row) < SCREEN_HEIGHT) {
          TFT_display.drawPixel(x + col + i, y + row, color);
        }
      }
    }
  }

  bmpFile.close();  // Close file when done
  Monitor.println("Finished drawing BMP");
}

// Function to get BMP image dimensions
bool getBMPDimensions(const char *filename, uint32_t &width, uint32_t &height) {
  File bmpFile = SD.open(filename);
  if (!bmpFile) {
    Monitor.println("File not found");
    return false;
  }

  if (read16(bmpFile) != 0x4D42) {  // Check BMP signature
    Monitor.println("Not a BMP file");
    bmpFile.close();
    return false;
  }

  read32(bmpFile);  // Skip file size
  read32(bmpFile);  // Skip reserved bytes
  uint32_t imageOffset = read32(bmpFile);  // Start of image data

  uint32_t dibHeaderSize = read32(bmpFile);  // DIB header size

  // Read the width and height of the image
  width = read32(bmpFile);
  int32_t bmpHeight = readS32(bmpFile);  // May be negative for top-down images
  height = (bmpHeight < 0) ? -bmpHeight : bmpHeight;

  bmpFile.close();  // Close the file
  return true;      // Success
  delay(20000);
}

Credits

R. Scott Coppersmith
18 projects • 25 followers
Research Engineer, Embedded Systems Designer

Comments