#include <SPI.h>
#include <SD.h>
#include <PNGdec.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <math.h>
// KONFIGURACJA SPRZĘTOWA
#define TFT_MOSI 11 // GPIO11
#define TFT_SCLK 12 // GPIO12
#define TFT_MISO 13 // GPIO13
#define TFT_DC 2 // może zostać 2 – zwykły GPIO
#define TFT_RST 4 // reset TFT
#define TFT_CS 5 // CS wyświetlacza
// Karta SD (HSPI)
#define SD_MOSI 14 // GPIO14
#define SD_MISO 15 // GPIO15
#define SD_SCLK 16 // GPIO16
#define SD_CS 17 // GPIO17
// Kolory
#define TFT_COLOR1 0x6519
#define TFT_BLACK 0x0000
#define TFT_BLUE 0x001F
#define TFT_SKYBLUE 0x867D
#define TFT_DKGREEN 0x03E0
#define TFT_SILVER 0xC618
#define TFT_YELLOW 0xFFE0
#define TFT_DKYELLOW 0x8400
#define TFT_RED 0xF800
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
SPIClass spiSD(HSPI);
// biel jest traktowana jako kolor przezroczysty i nie jest rysowana na wyświetlaczu
#define TRANSPARENT_COLOR 0xF81F // fuksja, raczej nie ma jej na Księżycu :)
// --- KLUCZOWA POPRAWKA: Rozmiar bufora dla rotacji (240px dla 164px obrazu) ---
#define MAX_IMAGE_WIDTH 164
#define MAX_IMAGE_HEIGHT 164
// --- ZMIENNE DLA PNG ---
PNG png;
const char TEST_FILENAME[] = "/16.png"; // Zmień nazwę pliku, jeśli trzeba
int imageWidth = 0; // Będzie 164
int imageHeight = 0; // Będzie 164
uint16_t *originalImage[MAX_IMAGE_HEIGHT] = {NULL};
uint16_t *rotatedImage[MAX_IMAGE_HEIGHT] = {NULL};
// DEKLARACJE FUNKCJI
int pngDraw(PNGDRAW *pDraw);
bool loadPngFromSD(const char *filename);
void freeImageBuffers();
void rotateImage(float angle);
void displayRotatedImage(int x, int y);
// SETUP
void setup() {
Serial.begin(115200);
delay(1000);
// 1. Inicjalizacja SD
spiSD.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
if (!SD.begin(SD_CS, spiSD)) {
Serial.println("Błąd karty SD!");
}
// 2. Inicjalizacja TFT
SPI.begin(TFT_SCLK, TFT_MISO, TFT_MOSI);
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
tft.drawRect(0, 0, tft.width(), tft.height(), TFT_BLUE);
// 3. Ładowanie obrazu
if (loadPngFromSD(TEST_FILENAME)) {
Serial.printf("Obraz wczytany: %s (%dx%d)\n", TEST_FILENAME, imageWidth, imageHeight);
} else {
Serial.printf("Błąd wczytywania %s\n", TEST_FILENAME);
}
}
// LOOP
void loop() {
static unsigned long lastUpdate = 0;
static float angle = 0.0;
// Zwiększamy interwał z 50ms na 200ms, by dać czas na rysowanie i komunikację szeregową
const unsigned long UPDATE_INTERVAL = 10000;
if (millis() - lastUpdate > UPDATE_INTERVAL) {
// Czyścimy obszar Księżyca (górny fragment ekranu)
tft.fillRect(0, 0, 240, 200, TFT_BLACK);
angle += 5.0;
if (angle >= 360.0) angle = 0.0;
// Obliczenie offsetu bufora rotacji (38)
const int OFFSET = (MAX_IMAGE_WIDTH - imageWidth) / 2;
int centerX = (tft.width() - imageWidth) / 2; // (320-164)/2 = 78
int centerY = (tft.height() - imageHeight) / 2; // (240-164)/2 = 38
rotateImage(angle);
displayRotatedImage(centerX, centerY);
// Rysowanie ramki 164x164
//tft.drawRect(centerX - 1, centerY - 1, imageWidth + 2, imageHeight + 2, TFT_YELLOW);
Serial.printf("Kąt: %.1f, Rysowanie od X:%d, Y:%d\n", angle, centerX - OFFSET, centerY - OFFSET);
lastUpdate = millis();
}
}
// IMPLEMENTACJE FUNKCJI OBRAZU
// Funkcja rysująca linię PNG do bufora originalImage
int pngDraw(PNGDRAW *pDraw) {
uint16_t lineBuffer[MAX_IMAGE_WIDTH];
// Użycie TFT_BLACK jako koloru przezroczystości podczas dekodowania
png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_LITTLE_ENDIAN, TFT_BLACK);
if (pDraw->y < imageHeight && originalImage[pDraw->y] != NULL) {
memcpy(originalImage[pDraw->y], lineBuffer, pDraw->iWidth * 2);
}
return 1;
}
bool loadPngFromSD(const char *filename) {
Serial.printf("Ładuję: %s\n", filename);
File f = SD.open(filename, "r");
if (!f) {
Serial.println("Błąd otwarcia pliku!");
return false;
}
int sz = f.size();
uint8_t *buf = (uint8_t*)malloc(sz);
if (!buf) {
f.close();
return false;
}
f.read(buf, sz);
f.close();
int rc = png.openRAM(buf, sz, pngDraw);
if (rc == PNG_SUCCESS) {
imageWidth = png.getWidth();
imageHeight = png.getHeight();
Serial.printf("PNG: %dx%d, MAX_IMAGE_HEIGHT=%d\n", imageWidth, imageHeight, MAX_IMAGE_HEIGHT);
// DEBUG: sprawdź zakres
if (imageHeight > MAX_IMAGE_HEIGHT) {
Serial.printf("BŁĄD: imageHeight=%d > MAX=%d\n", imageHeight, MAX_IMAGE_HEIGHT);
}
// Alokacja
for (int i = 0; i < imageHeight; i++) {
if (i >= MAX_IMAGE_HEIGHT) {
Serial.printf("BŁĄD: i=%d >= MAX=%d\n", i, MAX_IMAGE_HEIGHT);
break;
}
originalImage[i] = (uint16_t*)malloc(imageWidth * 2);
rotatedImage[i] = (uint16_t*)malloc(MAX_IMAGE_WIDTH * 2);
if (!originalImage[i] || !rotatedImage[i]) {
Serial.printf("BŁĄD alokacji wiersza %d\n", i);
}
}
Serial.printf("Zaallocowano %d wierszy\n", imageHeight);
png.decode(NULL, 0);
}
return rc == PNG_SUCCESS;
}
// obrót obrazu o dowolny kąt (KLUCZOWA POPRAWKA CENTROWANIA W BUFORZE)
void rotateImage(float angle) {
if (imageWidth == 0 || imageHeight == 0)
return;
float rad = angle * M_PI / 180.0;
float s = sin(rad);
float c = cos(rad);
float cx = imageWidth / 2.0;
float cy = imageHeight / 2.0;
// Wyczyść bufor rotacji (na kolor przezroczysty)
for (int y = 0; y < MAX_IMAGE_HEIGHT; y++) {
if (rotatedImage[y]) {
for (int x = 0; x < MAX_IMAGE_WIDTH; x++) {
rotatedImage[y][x] = TFT_BLACK;
}
}
}
// REVERSE MAPPING — dla każdego piksela docelowego
for (int y = 0; y < imageHeight; y++) {
if (!rotatedImage[y])
continue;
for (int x = 0; x < imageWidth; x++) {
// Oblicz źródłowe współrzędne w oryginalnym obrazie
float sx = c * (x - cx) + s * (y - cy) + cx;
float sy = -s * (x - cx) + c * (y - cy) + cy;
int isx = (int)(sx + 0.5f);
int isy = (int)(sy + 0.5f);
// Jeśli w granicach — przepisz piksel
if (isx >= 0 && isx < imageWidth && isy >= 0 && isy < imageHeight &&
originalImage[isy]) {
uint16_t pix = originalImage[isy][isx];
if (pix != TFT_BLACK) { // nie kopiuj przezroczystego
rotatedImage[y][x] = pix;
}
}
}
}
}
// wyświetlenie obróconego obrazu na TFT
void displayRotatedImage(int x, int y) {
for (int r = 0; r < MAX_IMAGE_HEIGHT; r++) {
if (!rotatedImage[r])
continue;
for (int c = 0; c < MAX_IMAGE_WIDTH; c++) {
uint16_t col = rotatedImage[r][c];
if (col != TFT_BLACK) {
tft.drawPixel(x + c, y + r, col);
}
}
}
}
// zwolnienie pamięci (dla czystości)
void freeImageBuffers() {
for (int i = 0; i < MAX_IMAGE_HEIGHT; i++) {
if (originalImage[i]) free(originalImage[i]);
if (rotatedImage[i]) free(rotatedImage[i]);
originalImage[i] = NULL;
rotatedImage[i] = NULL;
}
}Loading
esp32-s3-devkitc-1
esp32-s3-devkitc-1