Panorama Pixeli

Typ_projektu
Arduino
Zdjecie główne
Krótki opis projektu

Poznajcie świat pixeli ! Projekt przedstawia animowaną panoramę miasta na matrycy LED 16x16 z dynamicznym przejściem dnia i nocy. Samochód porusza się po dolnej krawędzi ekranu, a w tle przesuwają się budynki, drzewa i w zależności od pory dnia widoczne są chmury lub gwiazdy.

Niezbędne elementy

Ramka SIC PixelArt 

Sprzęt

Komputer PC z Arduino IDE

Opis projektu

Projekt przedstawia animowaną panoramę miasta wyświetlaną na matrycy LED o wymiarach 16×16 pikseli. Obraz na matrycy jest dynamiczny – panorama miasta przesuwa się tworząc wrażenie ruchu. W tle widoczne są szare budynki oraz drzewa. Panorama zmienia się w zależności od pory dnia, w dzień widoczne są białe chmury, zaś w nocy pojawiają się gwiazdy.

Dodatkowym elementem animacji jest czerwony samochód, który co jakiś czas przejeżdża wzdłuż dolnej części matrycy.

Zdjęcia
kod programu
#include <Adafruit_NeoPixel.h>


 

#define PIN 15                   // Pin do którego podłączona jest matryca
#define WIDTH 16                // Szerokość matrycy
#define HEIGHT 16               // Wysokość matrycy
#define NUMPIXELS (WIDTH * HEIGHT)


 

Adafruit_NeoPixel strip(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);


 

// Mapowanie współrzędnych (Z-shape)
int getPixelIndex(int x, int y) {
  if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return -1;
  return (y % 2 == 0) ? y * WIDTH + x : y * WIDTH + (WIDTH - 1 - x);
}


 

#define PANORAMA_WIDTH 64       // Szerokość przesuwanej panoramy
uint32_t colors[PANORAMA_WIDTH][HEIGHT];


 

int offset = 0;                 // Pozycja przesunięcia panoramy


 

// Samochód
int carX = -4;
const int carWidth = 3;
const int carY = HEIGHT - 3;
int loopCounter = 0;
bool carVisible = false;


 

// Tryb dzień/noc
bool nightMode = false;
int nightTransitionCounter = 0;
bool panoramaGenerated = false;


 

// Rysowanie samochodu na ekranie
void drawCar() {
  if (!carVisible) return;


 

  // Górna część samochodu
  for (int dx = 0; dx < 2; dx++) {
    int x = carX + dx;
    if (x >= 0 && x < WIDTH) {
      strip.setPixelColor(getPixelIndex(x, carY), strip.Color(255, 0, 0));
    }
  }


 

  // Dolna część samochodu
  for (int dx = 0; dx < carWidth; dx++) {
    int x = carX + dx;
    if (x >= 0 && x < WIDTH) {
      strip.setPixelColor(getPixelIndex(x, carY + 1), strip.Color(255, 0, 0));
    }
  }


 

  // Koła samochodu
  int wheelY = carY + 2;
  int leftWheelX = carX;
  int rightWheelX = carX + carWidth - 1;


 

  if (leftWheelX >= 0 && leftWheelX < WIDTH)
    strip.setPixelColor(getPixelIndex(leftWheelX, wheelY), strip.Color(0, 0, 0));
  if (rightWheelX >= 0 && rightWheelX < WIDTH)
    strip.setPixelColor(getPixelIndex(rightWheelX, wheelY), strip.Color(0, 0, 0));
}


 

// Generowanie panoramy (tło, budynki, drzewa, gwiazdy itp.)
void generatePanorama() {
  uint32_t skyColor = nightMode ? strip.Color(0, 0, 40) : strip.Color(100, 150, 255);
  uint32_t cloudColor = strip.Color(255, 255, 255);


 

  // Wypełnienie tła kolorem nieba
  for (int x = 0; x < PANORAMA_WIDTH; x++) {
    for (int y = 0; y < HEIGHT; y++) {
      colors[x][y] = skyColor;
    }
  }


 

  // Rysowanie chmur w dzień
  if (!nightMode) {
    int cloudCount = random(3, 6);
    for (int i = 0; i < cloudCount; i++) {
      int cx = random(1, PANORAMA_WIDTH - 5);
      int cy = random(0, 3);


 

      for (int dx = -2; dx <= 1; dx++) {
        int x = cx + dx;
        int y = cy + 1;
        if (x >= 0 && x < PANORAMA_WIDTH && y < HEIGHT) {
          colors[x][y] = cloudColor;
        }
      }


 

      for (int dx = -1; dx <= 0; dx++) {
        int x = cx + dx;
        int y = cy;
        if (x >= 0 && x < PANORAMA_WIDTH && y < HEIGHT) {
          colors[x][y] = cloudColor;
        }
      }
    }
  }


 

  // Rysowanie gwiazd w nocy
  if (nightMode) {
    int starCount = random(10, 20);
    for (int i = 0; i < starCount; i++) {
      int x = random(0, PANORAMA_WIDTH);
      int y = random(0, 5);
      colors[x][y] = strip.Color(255, 255, 0);
    }
  }


 

  // Budynki i drzewa
  int x = 0;
  while (x < PANORAMA_WIDTH) {
    int choice = random(0, 5);


 

    if (choice < 3) {
      // Budynek
      int buildingWidth = random(2, 5);
      int buildingHeight = random(6, 14);
      int top = HEIGHT - buildingHeight;


 

      for (int i = 0; i < buildingWidth && (x + i) < PANORAMA_WIDTH; i++) {
        for (int y = top; y < HEIGHT; y++) {
          colors[x + i][y] = strip.Color(80, 80, 80);
        }
      }


 

      x += buildingWidth + random(1, 2);
    } else {
      // Drzewo
      int trunkX = x;
      int trunkHeight = random(1, 4);
      int trunkBottomY = HEIGHT - 1;


 

      // Pień drzewa
      for (int i = 0; i < trunkHeight; i++) {
        int y = trunkBottomY - i;
        if (y >= 0) {
          colors[trunkX][y] = strip.Color(139, 69, 19);
        }
      }


 

      // Korona drzewa (2x3 + 1 piksel na górze)
      int crownHeight = 2;
      int crownWidth = 3;
      int crownTopY = trunkBottomY - trunkHeight - (crownHeight - 1);


 

      for (int dx = -1; dx <= 1; dx++) {
        for (int dy = 0; dy < 2; dy++) {
          int cx = trunkX + dx;
          int cy = crownTopY + dy;
          if (cx >= 0 && cx < PANORAMA_WIDTH && cy >= 0 && cy < HEIGHT) {
            colors[cx][cy] = strip.Color(0, 200 + random(0, 56), 0);
          }
        }
      }
      int topCenterX = trunkX;
      int topCenterY = crownTopY - 1;
      if (topCenterX >= 0 && topCenterX < PANORAMA_WIDTH && topCenterY >= 0 && topCenterY < HEIGHT) {
        colors[topCenterX][topCenterY] = strip.Color(0, 200 + random(0, 56), 0);
      }


 

      x += crownWidth + 1;
    }
  }
}


 

// Wyświetlenie aktualnej klatki panoramy
void drawFrame() {
  strip.clear();


 

  for (int x = 0; x < WIDTH; x++) {
    for (int y = 0; y < HEIGHT; y++) {
      int panoX = (x + offset) % PANORAMA_WIDTH; // Obliczenie pozycji X w panoramie (przesuwane tło)
      strip.setPixelColor(getPixelIndex(x, y), colors[panoX][y]);
    }
  }
}


 

void setup() {
  strip.begin();
  strip.setBrightness(100);
  strip.show();
  generatePanorama();
  panoramaGenerated = true;
}


 

void loop() {
  drawFrame();
  drawCar();
  strip.show();


 

  // Przesunięcie panoramy 
  offset++;
  if (offset >= PANORAMA_WIDTH) {
    offset = 0;
  }


 

  // Obsługa samochodu
  loopCounter++;
  if (loopCounter % 64 == 0) {
    carX = -carWidth;
    carVisible = true;
  }


 

  if (carVisible) {
    carX++;
    if (carX > WIDTH + carWidth) {
      carVisible = false;
    }
  }


 

  // Przełączanie dnia i nocy co 200 pętli
  nightTransitionCounter++;
  if (nightTransitionCounter >= 200) {
    nightTransitionCounter = 0;
    nightMode = !nightMode;
    generatePanorama();
  }


 

  delay(100);
}
Pliki_projektu
Schemat
Youtube
Tagi
LED, piksele, SIC PixelArt, matryca, miasto