
To projekt wykorzystujący matrycę LED 16x16 do wyświetlania zadanych stanów pogody w formie animowanego PixelArtu. System obsługuje 5 różnych typów pogody, z których każdy posiada unikalną animację. Dodatkowo wyświetlana jest temperatura w zakresie od -9 do 99 stopni Celsjusza. Informacje o pogodzie i temperaturze są przesyłane do urządzenia za pomocą komend wpisywanych w Serial Monitor w Arduino IDE.
Ramka SIC PixelArt
Komputer PC z Arduino IDE
Projekt został zrealizowany przy pomocy mikrokontrolera ESP32 DevKit oraz matrycy LED PixelArt 16x16. Za pomocą prostych komend wpisywanych w monitorze portu szeregowego możliwe jest wyświetlanie sześciu różnych typów pogody, każdej z własną animacją, oraz aktualnej temperatury w zakresie od -9°C do 99°C.
Obsługiwane typy pogody:
-
s
– Słońce z promieniami poruszającymi się cyklicznie -
r
– Deszcz z dynamicznie spadającymi kroplami -
t
– Burza z efektem błyskawicy i migającego światła -
n
– Śnieg z opadającymi płatkami -
f
– Mgła z animowaną warstwą o zmiennym natężeni
Temperatura:
Temperatura wyświetlana jest w prawym dolnym rogu matrycy. Jest to pismo bitmapowe. Pozwala ona na ustawianie temperatury w zakresie -9 do 99 stopni Celcjusza. Można ją zmienić poprzez wpisywanie jej w Serial Monitor
Interakcja:
Wszystkie funkcje projektu obsługiwane są z poziomu Serial Monitor:
-
Litera (
s
,r
,t
,n
,) zmienia aktualnie wyświetlaną pogodę. -
Wpisanie liczby (np.
23
,-4
) ustawia wyświetlaną temperaturę.
Zastosowane funkcje:
-
getPixelIndex(x, y)
– konwertuje współrzędne (x, y) do indeksu LED-a w układzie ZIG-ZAG -
clearMatrix()
– czyści ekran -
drawCloud(...)
– uniwersalne rysowanie chmur -
drawBitmapDigit(...)
– wyświetlanie cyfr (do temperatury) -
drawMinus(...)
– rysowanie znaku minus -
drawTemperature(temp)
– główna funkcja wyświetlania temperatury -
animateSun()
– animacja słoneczna -
animateRain()
– animacja deszczu -
animateStorm()
– animacja burzy z błyskawicą -
animateSnow()
– animacja śniegu -
animateFog()
– animacja mgły





#include <Adafruit_NeoPixel.h>
#define LED_PIN 15
#define LED_WIDTH 16
#define LED_HEIGHT 16
#define NUM_LEDS (LED_WIDTH * LED_HEIGHT)
Adafruit_NeoPixel matrix(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
int currentTemp = 23; // wartość temperatury do wyświetlenia
// === Konwersja (x, y) → indeks LED-a (ZIG-ZAG) ===
int getPixelIndex(int x, int y) {
if (x < 0 || x >= LED_WIDTH || y < 0 || y >= LED_HEIGHT) return -1;
// Odbicie lustrzane X względem osi pionowej
// Ponieważ (0,0) jest w prawym górnym rogu
x = LED_WIDTH - 1 - x;
if (y % 2 == 0)
return y * LED_WIDTH + x;
else
return y * LED_WIDTH + (LED_WIDTH - 1 - x);
}
void clearMatrix() {
matrix.clear();
}
/////////////////////////////////////////////////////////////////////
// === Uniwersalne rysowanie chmury ===
void drawCloud(int baseX, int baseY, uint32_t color, int width = 8, int height = 4) {
for (int x = baseX; x < baseX + width; x++) {
for (int y = 0; y < height; y++) {
if ((x == baseX || x == baseX + width - 1) && y == 0) continue;
int idx = getPixelIndex(x, baseY + y);
if (idx >= 0) matrix.setPixelColor(idx, color);
}
}
}
/////////////////////////////////////////////////////////////////
const byte digitBitmap[10][5] = {
{ B111, B101, B101, B101, B111 }, // 0
{ B010, B110, B010, B010, B111 }, // 1
{ B111, B001, B111, B100, B111 }, // 2
{ B111, B001, B111, B001, B111 }, // 3
{ B101, B101, B111, B001, B001 }, // 4
{ B111, B100, B111, B001, B111 }, // 5
{ B111, B100, B111, B101, B111 }, // 6
{ B111, B001, B010, B010, B010 }, // 7
{ B111, B101, B111, B101, B111 }, // 8
{ B111, B101, B111, B001, B111 } // 9
};
void drawBitmapDigit(int digit, int baseX, int baseY, uint32_t color) {
if (digit < 0 || digit > 9) return;
for (int row = 0; row < 5; row++) {
byte bits = digitBitmap[digit][row];
for (int col = 0; col < 3; col++) {
if (bits & (1 << (2 - col))) { // sprawdzamy bit od lewej
int x = baseX-1 + col;
int y = baseY + row;
int idx = getPixelIndex(x, y);
if (idx >= 0) matrix.setPixelColor(idx, color);
}
}
}
}
////////////////////////////////////////////////////////////////////////
void drawMinus(int baseX, int baseY, uint32_t color) {
for (int col = 0; col < 3; col++) {
int x = baseX - 1 + col;
int y = baseY + 2; // środek symbolu "-"
int idx = getPixelIndex(x, y);
if (idx >= 0) matrix.setPixelColor(idx, color);
}
}
void drawTemperature(int temp) {
uint32_t color = matrix.Color(255, 255, 255);
if (temp < 0) {
drawMinus(10, 11, color);
drawBitmapDigit(abs(temp), 14, 11, color);
} else {
int dzies = temp / 10;
int jedn = temp % 10;
if (temp < 10) {
drawBitmapDigit(jedn, 14, 11, color);
} else {
drawBitmapDigit(dzies, 10, 11, color);
drawBitmapDigit(jedn, 14, 11, color);
}
}
}
// === Animacje ===/////////////////////////////////////////////////////
void animateSun() {
static int frame = 0;
clearMatrix();
int cx = 7, cy = 7;
uint32_t sunColor = matrix.Color(255, 200, 50);
for (int dx = 0; dx <= 2; dx++) {
for (int dy = 0; dy <= 2; dy++) {
int idx = getPixelIndex(cx - 1 + dx, cy - 1 + dy);
if (idx >= 0) matrix.setPixelColor(idx, sunColor);
}
}
const int rays[8][2][2] = {
{ { 0, -3 }, { 0, -4 } }, { { 2, -2 }, { 3, -3 } }, { { 3, 0 }, { 4, 0 } }, { { 2, 2 }, { 3, 3 } },
{ { 0, 3 }, { 0, 4 } }, { { -2, 2 }, { -3, 3 } }, { { -3, 0 }, { -4, 0 } }, { { -2, -2 }, { -3, -3 } }
};
int group = frame % 2;
for (int i = 0; i < 8; i++) {
bool isDiagonal = (i % 2 == 1);
if (isDiagonal == (group == 1)) {
for (int j = 0; j < 2; j++) {
int x = cx + rays[i][j][0];
int y = cy + rays[i][j][1];
int idx = getPixelIndex(x, y);
if (idx >= 0) matrix.setPixelColor(idx, sunColor);
}
}
}
drawTemperature(currentTemp);
matrix.show();
frame = (frame + 1) % 2;
delay(250);
}
//////////////////////////////////////////////////////////////////
void animateStorm() {
static int frame = 0;
clearMatrix();
uint32_t gray = matrix.Color(60, 60, 60);
uint32_t yellow = matrix.Color(255, 255, 100);
uint32_t flashColor = matrix.Color(100, 100, 100);
if (frame % 12 == 0) {
for (int i = 0; i < NUM_LEDS; i++) matrix.setPixelColor(i, flashColor);
}
drawCloud(3, 3, gray);
if (frame % 6 < 3) {
int bolt[7][2] = {
{7, 5}, {8, 6}, {7, 7}, {8, 8}, {7, 9}, {8, 10}, {8, 11}
};
for (int i = 0; i < 7; i++) {
int idx = getPixelIndex(bolt[i][0], bolt[i][1]);
if (idx >= 0) matrix.setPixelColor(idx, yellow);
}
}
drawTemperature(currentTemp);
matrix.show();
frame++;
delay(160);
}
///////////////////////////////////////////////////////////
void animateRain() {
static int frame = 0;
clearMatrix();
uint32_t gray = matrix.Color(60, 60, 60);
uint32_t blue = matrix.Color(0, 120, 255);
uint32_t splash = matrix.Color(0, 80, 200);
drawCloud(3, 3, gray); // przesunięcie w lewo!
int columns[] = {4, 5, 6, 7}; // też przesuwamy kolumny deszczu!
for (int i = 0; i < 4; i++) {
int col = columns[i];
int dropY = 7 + ((frame + i * 4) % 8);
for (int j = 0; j < 4; j++) {
int y = dropY + j;
if (y < LED_HEIGHT - 1) {
int idx = getPixelIndex(col, y);
if (idx >= 0) matrix.setPixelColor(idx, blue);
} else if (y == LED_HEIGHT - 1) {
int splashIdx = getPixelIndex(col, y);
if (splashIdx >= 0) matrix.setPixelColor(splashIdx, splash);
}
}
}
drawTemperature(currentTemp);
matrix.show();
frame++;
delay(130);
}
////////////////////////////////////////////////////////////////////////////////
void animateSnow() {
static int frame = 0;
clearMatrix();
uint32_t gray = matrix.Color(80, 80, 80);
uint32_t white = matrix.Color(255, 255, 255);
drawCloud(3, 3, gray);
int flakes[] = {4, 5, 6, 7};
for (int i = 0; i < 4; i++) {
int col = flakes[i];
int flakeY = 7 + ((frame + i * 3) % 9);
int idx = getPixelIndex(col, flakeY);
if (idx >= 0) matrix.setPixelColor(idx, white);
}
drawTemperature(currentTemp);
matrix.show();
frame++;
delay(160);
}
///////////////////////////////////////////////////////////////////////////
void animateFog() {
static int frame = 0;
clearMatrix();
// Kolory mgły o różnym natężeniu (lekka, średnia, gęsta)
uint32_t lightFog = matrix.Color(60, 60, 60);
uint32_t midFog = matrix.Color(90, 90, 90);
uint32_t heavyFog = matrix.Color(130, 130, 130);
for (int y = 4; y <= 12; y++) {
for (int x = 0; x < LED_WIDTH; x++) {
int wave = (x + frame / 2) % 16;
uint32_t color;
if (wave < 5) color = lightFog;
else if (wave < 10) color = midFog;
else color = heavyFog;
// lekkie falowanie warstw mgły
int offsetY = (int)(1.5 * sin((x + frame) * 0.3));
int idx = getPixelIndex(x, y + offsetY);
if (idx >= 0) matrix.setPixelColor(idx, color);
}
}
drawTemperature(currentTemp);
matrix.show();
frame++;
delay(120);
}
///////////////////////////////////////////////////////////////////////////
void setup() {
matrix.begin();
matrix.setBrightness(50);
clearMatrix();
matrix.show();
Serial.begin(9600);
Serial.println("Wpisz: s (słońce), r (deszcz), t (burza), n (śnieg), f (mgła), m (ksiezyc) lub liczbę temperatury (np. 23):");
}
void loop() {
static char mode = 's';
static String inputBuffer = "";
while (Serial.available()) {
char c = Serial.read();
if (isdigit(c) || c == '-') { // <-- pozwalamy na '-'
inputBuffer += c;
} else if (c == '\n' || c == '\r') {
if (inputBuffer.length() > 0) {
int newTemp = inputBuffer.toInt(); // obsługuje również wartości ujemne
if (newTemp >= -9 && newTemp <= 99) { // zakres rozszerzony o minus
currentTemp = newTemp;
Serial.print("Ustawiono temperaturę: ");
Serial.println(currentTemp);
}
inputBuffer = "";
}
} else if (c == 's' || c == 'r' || c == 't'|| c == 'n' || c == 'f'|| c == 'm') {
mode = c;
}
}
if (mode == 's') animateSun();
else if (mode == 'r') animateRain();
else if (mode == 't') animateStorm();
else if (mode == 'n') animateSnow(); // 'n' jak "śnieg"
else if (mode == 'f') animateFog(); // 'm' jak "mgła"
}