Post the bmp file. I will guess you converted the color palette when you opened then saved the file. Try changing the file back to BMP, (16?) color palette.
#include <User_Setup.h>
#include <TFT_eSPI.h>
#include <SPIFFS.h>
TFT_eSPI tft = TFT_eSPI();
const int FRAME_DELAY = 100;
const char* eyeFrames[] = {"/eyes.bmp", "/eyes2.bmp", "/eyes3.bmp", "/eyes4.bmp"};
const int TOTAL_FRAMES = 4;
struct BMPHeader {
uint32_t fileSize;
uint32_t reserved;
uint32_t dataOffset;
uint32_t headerSize;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bitsPerPixel; // Harus 32 untuk ARGB
uint32_t compression;
uint32_t imageSize;
int32_t xPixelsPerM;
int32_t yPixelsPerM;
uint32_t colorsUsed;
uint32_t colorsImportant;
};
bool readBMPHeader(File &f, BMPHeader &header) {
// Check BMP signature
if (f.read() != 'B' || f.read() != 'M') {
return false;
}
header.fileSize = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.reserved = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.dataOffset = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.headerSize = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.width = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.height = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.planes = f.read() | (f.read() << 8);
header.bitsPerPixel = f.read() | (f.read() << 8);
header.compression = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.imageSize = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.xPixelsPerM = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.yPixelsPerM = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.colorsUsed = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
header.colorsImportant = f.read() | (f.read() << 8) | (f.read() << 16) | (f.read() << 24);
return true;
}
void drawBMP(const char *filename, int16_t x, int16_t y) {
File f = SPIFFS.open(filename, "r");
if (!f) {
Serial.println("Failed to open file");
return;
}
BMPHeader header;
if (!readBMPHeader(f, header)) {
Serial.println("Not a BMP file");
f.close();
return;
}
// Verify that we have a 32-bit BMP
if (header.bitsPerPixel != 32) {
Serial.println("Not a 32-bit BMP file");
f.close();
return;
}
// Skip to start of pixel data
f.seek(header.dataOffset);
// Calculate row size with padding (4 byte alignment)
int rowSize = ((4 * header.width + 3) & ~3);
uint8_t *line = new uint8_t[rowSize];
if (!line) {
Serial.println("Unable to allocate memory");
f.close();
return;
}
// Create display buffer for one row
uint16_t *displayLine = new uint16_t[header.width];
if (!displayLine) {
delete[] line;
Serial.println("Unable to allocate display buffer");
f.close();
return;
}
// Read image data bottom to top
for (int i = header.height - 1; i >= 0; i--) {
// Read entire line with padding
if (f.read(line, rowSize) != rowSize) {
Serial.println("Error reading line");
break;
}
// Convert line to RGB565
for (int j = 0; j < header.width; j++) {
// BMP 32-bit ARGB format
uint8_t b = line[j * 4]; // Blue is at offset 0
uint8_t g = line[j * 4 + 1]; // Green is offset by 1
uint8_t r = line[j * 4 + 2]; // Red is offset by 2
uint8_t a = line[j * 4 + 3]; // Alpha is offset by 3
// If pixel is not fully transparent
if (a > 0) {
// Apply alpha blending
r = (r * a) >> 8;
g = (g * a) >> 8;
b = (b * a) >> 8;
// Convert to RGB565 format
uint16_t pixel = ((r & 0xF8) << 8) | // 5 bits red
((g & 0xFC) << 3) | // 6 bits green
(b >> 3); // 5 bits blue
displayLine[j] = pixel;
} else {
// For fully transparent pixels, use background color
displayLine[j] = TFT_BLACK; // or any other background color
}
}
// Push the line to display
if (y + i >= 0 && y + i < tft.height()) {
tft.pushImage(x, y + i, header.width, 1, displayLine);
}
}
delete[] line;
delete[] displayLine;
f.close();
}
void setup() {
Serial.begin(115200);
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFS Mount Failed");
return;
}
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
}
void loop() {
static unsigned long lastFrameTime = 0;
static int currentFrame = 0;
static bool forward = true;
unsigned long currentTime = millis();
if (currentTime - lastFrameTime >= FRAME_DELAY) {
drawBMP(eyeFrames[currentFrame], 0, 0);
if (forward) {
currentFrame++;
if (currentFrame >= TOTAL_FRAMES) {
currentFrame = TOTAL_FRAMES - 2;
forward = false;
}
} else {
currentFrame--;
if (currentFrame < 0) {
currentFrame = 1;
forward = true;
}
}
lastFrameTime = currentTime;
}
}
user.setup.h
#ifndef USER_SETUP_H
#define USER_SETUP_H
// Setup untuk ST7735S
#define ST7735_DRIVER
// Untuk sebagian besar ST7735S, orientasi ini biasanya tepat
#define TFT_WIDTH 80
#define TFT_HEIGHT 160
// Color depth 16-bit (RGB565)
#define COLOR_DEPTH 16
// PENTING: setting warna untuk ST7735S
#define TFT_RGB // Ubah ke TFT_RGB jika warna masih terbalik
#define TFT_ROTATION 3 // Rotasi 3: orientasi layar horizontal dengan pin di bawah
// Pin SPI default - sesuaikan dengan wiring Anda
#define TFT_MOSI 19 // Pin data SPI (DIN)
#define TFT_SCLK 18 // Pin clock SPI (CLK)
#define TFT_CS 0 // Pin Chip Select
#define TFT_DC 2 // Pin Data/Command
#define TFT_RST 4 // Pin Reset (gunakan -1 jika tidak ada)
// Optimize speed
#define SPI_FREQUENCY 27000000
#define SPI_READ_FREQUENCY 20000000
// Offset layar - sesuaikan jika gambar tidak tepat di tengah
#define TFT_INVERSION_ON
#define ST7735_GREENTAB160x80
//#define ST7735_GREENTAB
//#define ST7735_BLACKTAB
// Font default (opsional)
#define LOAD_GLCD // Font 1. Original Adafruit 8 pixel font needs ~1820 bytes in FLASH
#define LOAD_FONT2 // Font 2. Small 16 pixel high font, needs ~3534 bytes in FLASH
#define LOAD_FONT4 // Font 4. Medium 26 pixel high font, needs ~5848 bytes in FLASH
#endif