BMP images displayed weirdly

I programmed an Arduino Nano to display bmp images on a lcd tft display.
The code I wrote is doing some other stuff too but that is not imporant the only thing thats not working is the displaying of the bmp images. If you have any questions about my code feel free to ask.

#include <MCUFRIEND_kbv.h>
#include <SD.h>
#include <SPI.h>

#define statusPin1 13
#define statusPin2 A5
#define SD_CS 10

#define BLACK   0x0000
#define RED     0xF800
#define GREEN   0x07E0
#define WHITE   0xFFFF

MCUFRIEND_kbv tft;
File bmpFile;

uint8_t currentPage = 0;
uint8_t lastPage = 255;

//y, x
const uint16_t imagePositions[6][2] = {
  {0, 350}, {0, 200}, {0, 10},
  {170, 350}, {170, 200}, {170, 10}
};

void drawBMP(const char *filename, int x, int y) {
  bmpFile = SD.open(filename);
  if (!bmpFile) {
    Serial.print("Fehlt: "); Serial.println(filename);
    return;
  }

  if (bmpFile.read() != 'B' || bmpFile.read() != 'M') {
    Serial.println("Kein BMP");
    bmpFile.close();
    return;
  }

  bmpFile.seek(10);
  uint32_t dataOffset = bmpFile.read() | (bmpFile.read() << 8) | (bmpFile.read() << 16) | (bmpFile.read() << 24);

  bmpFile.seek(18);
  int32_t bmpWidth  = bmpFile.read() | (bmpFile.read() << 8) | (bmpFile.read() << 16) | (bmpFile.read() << 24);
  int32_t bmpHeight = bmpFile.read() | (bmpFile.read() << 8) | (bmpFile.read() << 16) | (bmpFile.read() << 24);

  if (bmpWidth != 120 || bmpHeight != 120) {
    Serial.println("Wrong  BMP Size");
    bmpFile.close();
    return;
  }

  bmpFile.seek(dataOffset);

  uint8_t r, g, b;
  uint16_t color;

  int rowSize = (bmpWidth * 3 + 3) & ~3; 

  for (int row = 0; row < bmpHeight; row++) {
    int pos = dataOffset + (bmpHeight - 1 - row) * rowSize;
    bmpFile.seek(pos);

    for (int col = 0; col < bmpWidth; col++) {
      b = bmpFile.read();
      g = bmpFile.read();
      r = bmpFile.read();
      color = tft.color565(r, g, b);
      tft.drawPixel(x + col, y + row, color);
    }
  }

  bmpFile.close();
}

void showPage(uint8_t p) {
  tft.fillScreen(BLACK);
  int startIndex = (p - 1) * 6;

  for (int i = 0; i < 6; i++) {
    char filename[12];
    sprintf(filename, "/%02d.bmp", startIndex + i + 1);
    int x = imagePositions[i][0];
    int y = imagePositions[i][1];
    drawBMP(filename, x, y);
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(statusPin1, INPUT);
  pinMode(statusPin2, INPUT);

  uint16_t ID = tft.readID();
  tft.begin(ID);
  tft.setRotation(0);
  tft.fillScreen(BLACK);

  if (!SD.begin(SD_CS)) {
    tft.setTextColor(RED);
    tft.setTextSize(2);
    tft.setCursor(10, 10);
    tft.println("SD Card Initialization problem");
    while (1);
  }

  tft.setTextColor(GREEN);
  tft.setTextSize(2);
  tft.setCursor(10, 10);
  tft.println("Display ready");
}

void loop() {
  bool pin1 = digitalRead(statusPin1);
  bool pin2 = digitalRead(statusPin2);

  if (!pin1 && pin2){
      currentPage = 1; Serial.println("1");
  }else if (pin1 && !pin2){
    currentPage = 2; Serial.println("2");
  }else if (!pin1 && !pin2){
    currentPage = 3; Serial.println("3");
  }else if (pin1 && pin2){
    currentPage = 4; Serial.println("4");
  } 

  if (currentPage != lastPage) {
    lastPage = currentPage;
    showPage(currentPage);
  }

  delay(200);
}




Thank you for every comment!

1 Like

What was expected, and what was actual.

1 Like

I would clarify the boolean logic by testing only one button at a time. BTW, where is your bounce handling? Check out the IDE sample sketches for button library or bounce library.

1 Like

Hey, the code looks solid, but here are a few quick things to check:

  1. Make sure the BMPs are 24-bit uncompressed. If they’re saved in 16-bit, 32-bit, or anything compressed, they won’t show up.
  2. Keep file names short — like 01.bmp, 02.bmp. Arduino's SD library needs 8.3 format (max 8 letters before .bmp).
  3. Double-check the BMP size — your code expects 120x120 exactly. If the images aren’t that size, they’ll be skipped.

Try re-saving the BMPs as 24-bit with no compression, and confirm the filenames/sizes match what your code expects. Let me know if it still doesn’t work!

Thank you for all those answers I dont think. that the problem is related, to the files on the sd card because the following code works fine with drawing the BMP images but it has some other problems. Do you have any other ideas or should I just try to instead searching for the problem in the other code just copy the draw funcion from this code that works just fine with drawing the images?

#include "SD.h"
#include <SPI.h>
#include <MCUFRIEND_kbv.h>

MCUFRIEND_kbv tft;

// Farben definieren
#define BLACK   0x0000
#define BLUE    0x001F
#define RED     0xF800
#define GREEN   0x07E0
#define CYAN    0x07FF
#define MAGENTA 0xF81F
#define YELLOW  0xFFE0
#define WHITE   0xFFFF

#define PIXEL_NUMBER  1 
#define FILE_NUMBER 7
#define FILE_NAME_SIZE_MAX 20

int imageSize = 120;  

// Struktur zur Speicherung der Icon-Positionen
struct IconPosition { 
    int x;
    int y;
};

// Positionen für Icons
IconPosition iconPositions[6] = {
    {0, 0},  {0, 160}, { 0, 350},
    {160, 0}, {160, 160}, {160, 350}
};

int statusPin = A7;
int pageFlag = 0;
int pageFlagCurrent = 0;

uint32_t bmp_offset = 0;
uint16_t s_width, s_height;

// Dateinamen für Seite 1
char file_name1[FILE_NUMBER][FILE_NAME_SIZE_MAX] = {
    "splash.bmp", "01.bmp", "02.bmp", "03.bmp", "04.bmp", "05.bmp", "06.bmp"
};

// Dateinamen für Seite 2
char file_name2[FILE_NUMBER][FILE_NAME_SIZE_MAX] = {
    "splash.bmp", "07.bmp", "08.bmp", "09.bmp", "10.bmp", "11.bmp", "12.bmp"
};

// Dateinamen für Seite 3
char file_name3[FILE_NUMBER][FILE_NAME_SIZE_MAX] = {
    "splash.bmp", "13.bmp", "14.bmp", "15.bmp", "16.bmp", "17.bmp", "18.bmp"
};

uint16_t read_16(File fp) {
    uint8_t low;
    uint16_t high;
    low = fp.read();
    high = fp.read();
    return (high << 8) | low;
}

uint32_t read_32(File fp) {
    uint16_t low;
    uint32_t high;
    low = read_16(fp);
    high = read_16(fp);
    return (high << 8) | low;
}

bool analysis_bpm_header(File fp) {
    if (read_16(fp) != 0x4D42) {
        return false;
    }
    read_32(fp);
    read_32(fp);
    bmp_offset = read_32(fp);
    read_32(fp);
    read_32(fp);
    read_32(fp);
    read_16(fp);
    return true;
}

void errorCheck(File bmp_file) {
    if (!bmp_file) {
        tft.setTextColor(WHITE, BLUE);
        tft.setTextSize(1);
        tft.setCursor(0, 10);
        tft.print("didn't find BMP image!");
        while (1);
    }
    if (!analysis_bpm_header(bmp_file)) {
        tft.setTextColor(WHITE, BLUE);
        tft.setTextSize(1);
        tft.setCursor(0, 0);
        tft.print("bad bmp picture!");
        return;
    }
}

void draw_icon(File bmp_file, int x, int y) {
    uint16_t i, j;
    uint8_t bpm_data[3] = {0};
    uint16_t color;

    bmp_file.seek(bmp_offset);
    for (i = 0; i < imageSize; i++) {
        for (j = 0; j < imageSize; j++) {
            bmp_file.read(bpm_data, 3);
            color = tft.color565(bpm_data[2], bpm_data[1], bpm_data[0]);
            tft.drawPixel(x + j, (s_height - 1) - (y + i), color);
        }
    }
    bmp_file.close();
}

void setup() {
    Serial.begin(9600);
    uint16_t ID = tft.readID();
    if (ID == 0x9486 || ID == 0xFFFF) {
        tft.begin(0x9486);
    } else {
        tft.begin(ID);
    }

    tft.setRotation(0);
    tft.fillScreen(BLACK);

    s_width = tft.width();
    s_height = tft.height();

    pinMode(10, OUTPUT);
    pinMode(statusPin, OUTPUT);
    delay(100);

    if (!SD.begin(SS)) {
        tft.setTextColor(WHITE, BLUE);
        tft.setTextSize(1);
        tft.setCursor(0, 0);
        tft.print("SD Card Init fail!");
        while (1);
    }

    File bmp_file = SD.open(file_name1[0]);
    errorCheck(bmp_file);
    bmp_file.close();
    delay(1000);
}

void loop() {
    File bmp_file;
    int i;

    int pwmValue = analogRead(statusPin) / 4;  // 0–1023 → 0–255

    Serial.print("PWM-Wert (skaliert): ");
    Serial.println(pwmValue);

    int newPage = 0;

    if (abs(pwmValue - 0) <= 10) {
        newPage = 1;
    } else if (abs(pwmValue - 127) <= 10) {
        newPage = 2;
    } else if (abs(pwmValue - 255) <= 10) {
        newPage = 3;
    }

    if (newPage != 0 && newPage != pageFlagCurrent) {
        pageFlag = newPage;
        pageFlagCurrent = newPage;

        if (pageFlag == 1) {
            for (i = 1; i <= 6; i++) {
                bmp_file = SD.open(file_name1[i]);
                errorCheck(bmp_file);
                draw_icon(bmp_file, iconPositions[i - 1].x, iconPositions[i - 1].y);
            }
        } else if (pageFlag == 2) {
            for (i = 1; i <= 6; i++) {
                bmp_file = SD.open(file_name2[i]);
                errorCheck(bmp_file);
                draw_icon(bmp_file, iconPositions[i - 1].x, iconPositions[i - 1].y);
            }
        } else if (pageFlag == 3) {
            for (i = 1; i <= 6; i++) {
                bmp_file = SD.open(file_name3[i]);
                errorCheck(bmp_file);
                draw_icon(bmp_file, iconPositions[i - 1].x, iconPositions[i - 1].y);
            }
        }
    }

    delay(100);
}

Do you get the correct values for the offset, width, and height? Might need to cast the read value to a 32-bit type for the bit shift to work correctly.

Here a image that should be more clear on what is acually happening with the bmps


    int pos = dataOffset + (bmpHeight - 1 - row) * rowSize;

With a 120x120 pixel image, and an offset of 54, which gives a rowSize of 360, the equation produces an answer of 43254, which will not fit in an int. Change pos to int32_t.

1 Like