#include #include #include #include #include // Do losowania pozycji #define WIFI_SSID "" #define WIFI_PASSWORD "" #define BOT_TOKEN "" #define PANEL_RES_X 32 #define PANEL_RES_Y 16 #define PANEL_CHAIN 1 MatrixPanel_I2S_DMA *dma_display = nullptr; WiFiClientSecure secured_client; UniversalTelegramBot bot(BOT_TOKEN, secured_client); uint16_t myBLACK, myWHITE; String text = "Start!"; int textXPosition; int textYPosition; int scrollSpeed = 80; int colorOffset = 0; unsigned long lastScrollTime = 0; #define FONT_SIZE 1 int currentTheme = 2; // 0 = Czysty tekst, 1 = Serca, 2 = Świąteczny, 3 = Sylwestrowy enum ColorMode { RAINBOW, RED, GREEN, BLUE, WHITE, ORANGE, PINK, CUSTOM }; ColorMode currentColorMode = RAINBOW; const uint8_t heartSymbol2[5] = { 0b01010, 0b11111, 0b11111, 0b01110, 0b00100 }; const uint8_t treeSymbol[5] = { 0b00100, // * 0b01110, // *** 0b11111, // ***** 0b11111, // ***** 0b00100 // * }; const uint8_t starSymbol[3] = { 0b010, 0b111, 0b010 }; struct Snowflake { int x, y; bool active; }; #define MAX_SNOWFLAKES 10 Snowflake snowflakes[MAX_SNOWFLAKES]; struct Firework { int x, y, size, timer; uint16_t color; bool active; }; #define MAX_FIREWORKS 10 Firework fireworks[MAX_FIREWORKS]; uint16_t colorWheel(uint8_t pos) { if (pos < 85) { return dma_display->color565(pos * 3, 255 - pos * 3, 0); } else if (pos < 170) { pos -= 85; return dma_display->color565(255 - pos * 3, 0, pos * 3); } else { pos -= 170; return dma_display->color565(0, pos * 3, 255 - pos * 3); } } // Funkcja interpolacji kolorów między różowym a czerwonym uint16_t interpolateColor(uint8_t progress) { uint8_t r = 255; // Stała wartość czerwieni uint8_t g = map(progress, 0, 255, 192, 0); // Zielony od 192 do 0 uint8_t b = map(progress, 0, 255, 203, 0); // Niebieski od 203 do 0 return dma_display->color565(r, g, b); } uint16_t customColor = dma_display->color565(255, 255, 255); // Domyślny biały uint16_t getColorForMode(int i) { switch (currentColorMode) { case RAINBOW: return colorWheel((i * 32 + colorOffset) % 256); case RED: return dma_display->color565(255, 0, 0); case GREEN: return dma_display->color565(0, 255, 0); case BLUE: return dma_display->color565(0, 0, 255); case WHITE: return dma_display->color565(255, 255, 255); case ORANGE: return dma_display->color565(255, 165, 0); case PINK: return dma_display->color565(255, 105, 180); case CUSTOM: return customColor; // Zwraca niestandardowy kolor default: return dma_display->color565(255, 255, 255); } } void drawBitmap(int x, int y, const uint8_t bitmap[], uint16_t color, int height) { for (int i = 0; i < height; i++) { for (int j = 0; j < 5; j++) { if (bitmap[i] & (1 << (4 - j))) { dma_display->drawPixel(x + j, y + i, color); } } } } void drawFirework(int x, int y, int size, uint16_t color) { for (int i = -size; i <= size; i++) { dma_display->drawPixel(x + i, y, color); // Linia pozioma dma_display->drawPixel(x, y + i, color); // Linia pionowa } } void updateSnowflakes() { for (int i = 0; i < MAX_SNOWFLAKES; i++) { if (!snowflakes[i].active) { if (random(0, 10) > 7) { snowflakes[i] = {random(0, PANEL_RES_X - 3), 0, true}; } } else { dma_display->drawPixel(snowflakes[i].x, snowflakes[i].y, myBLACK); snowflakes[i].y++; if (snowflakes[i].y >= PANEL_RES_Y) { snowflakes[i].active = false; } else { drawBitmap(snowflakes[i].x, snowflakes[i].y, starSymbol, myWHITE, 3); } } } } void updateFireworks() { for (int i = 0; i < MAX_FIREWORKS; i++) { if (!fireworks[i].active) { if (random(0, 10) > 7) { int yPosition = (random(0, 2) == 0) ? random(0, 5) : random(12, 16); fireworks[i] = {random(0, PANEL_RES_X), yPosition, random(1, 3), random(5, 15), colorWheel(random(0, 255)), true}; } } else { if (fireworks[i].timer > 0) { fireworks[i].timer--; drawFirework(fireworks[i].x, fireworks[i].y, fireworks[i].size, fireworks[i].color); } else { fireworks[i].active = false; drawFirework(fireworks[i].x, fireworks[i].y, fireworks[i].size, myBLACK); } } } } void drawSymbols() { // Czyszczenie górnej i dolnej części wyświetlacza dma_display->fillRect(0, 0, dma_display->width(), 5, myBLACK); dma_display->fillRect(0, 12, dma_display->width(), 4, myBLACK); // Obliczanie postępu interpolacji (od 0 do 255) uint8_t progress = (millis() / 10) % 512; if (progress > 255) { progress = 511 - progress; } uint16_t color1 = interpolateColor(progress); uint16_t color2 = interpolateColor(255 - progress); switch (currentTheme) { case 0: // Czysty tekst break; case 1: // Serca for (int i = 0; i < 6; i++) { uint16_t color = (i % 2 == 0) ? color1 : color2; drawBitmap(i * 6, 0, heartSymbol2, color, 5); } for (int i = 0; i < 6; i++) { uint16_t color = (i % 2 == 0) ? color2 : color1; drawBitmap(i * 6, 12, heartSymbol2, color, 5); } break; case 2: // Świąteczny updateSnowflakes(); for (int i = 0; i < 4; i++) { drawBitmap(1 + i * 8, 0, treeSymbol, dma_display->color565(0, 255, 0), 5); // Choinki } for (int i = 0; i < 6; i++) { uint16_t color = (i % 2 == 0) ? myWHITE : dma_display->color565(255, 255, 0); drawBitmap(i * 6, 12, starSymbol, color, 3); // Gwiazdki } break; case 3: // Sylwestrowy updateFireworks(); break; } } void handleTelegramMessages(void *parameter) { while (true) { int numNewMessages = bot.getUpdates(bot.last_message_received + 1); if (numNewMessages > 0) { for (int i = 0; i < numNewMessages; i++) { String command = bot.messages[i].text; if (command.startsWith("/color ")) { String color = command.substring(7); // Obsługa niestandardowego formatu R,G,B if (color.indexOf(',') != -1) { int r = color.substring(0, color.indexOf(',')).toInt(); int g = color.substring(color.indexOf(',') + 1, color.lastIndexOf(',')).toInt(); int b = color.substring(color.lastIndexOf(',') + 1).toInt(); // Weryfikacja zakresu wartości RGB if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) { customColor = dma_display->color565(r, g, b); currentColorMode = CUSTOM; // Ustawia tryb niestandardowego koloru Serial.printf("Custom color set to: R=%d, G=%d, B=%d\n", r, g, b); } else { Serial.println("Invalid RGB values. Must be in range 0-255."); } } else { // Obsługa predefiniowanych kolorów if (color == "rainbow") currentColorMode = RAINBOW; else if (color == "red") currentColorMode = RED; else if (color == "green") currentColorMode = GREEN; else if (color == "blue") currentColorMode = BLUE; else if (color == "white") currentColorMode = WHITE; else if (color == "orange") currentColorMode = ORANGE; else if (color == "pink") currentColorMode = PINK; else Serial.println("Unknown color command."); } } else if (command.startsWith("/theme ")) { String theme = command.substring(7); if (theme == "blank") currentTheme = 0; else if (theme == "hearts") currentTheme = 1; else if (theme == "christmas") currentTheme = 2; else if (theme == "newyear") currentTheme = 3; } else if (command.startsWith("/brightness ")) { int brightness = command.substring(12).toInt(); if (brightness >= 0 && brightness <= 255) { dma_display->setBrightness8(brightness); Serial.printf("Brightness set to: %d\n", brightness); } else { Serial.println("Invalid brightness value. Must be in range 0-255."); } } else { text = command; textXPosition = dma_display->width(); } } } vTaskDelay(pdMS_TO_TICKS(1000)); // Oczekiwanie sekund przed kolejnym sprawdzeniem } } void setup() { Serial.begin(115200); HUB75_I2S_CFG mxconfig(PANEL_RES_X, PANEL_RES_Y, PANEL_CHAIN); mxconfig.clkphase = false; dma_display = new MatrixPanel_I2S_DMA(mxconfig); dma_display->begin(); dma_display->setBrightness8(150); dma_display->clearScreen(); dma_display->setTextWrap(false); myBLACK = dma_display->color565(0, 0, 0); myWHITE = dma_display->color565(255, 255, 255); Serial.print("Connecting to WiFi"); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); secured_client.setCACert(TELEGRAM_CERTIFICATE_ROOT); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } Serial.println("\nWiFi connected. IP address: " + WiFi.localIP().toString()); textXPosition = dma_display->width(); textYPosition = 5; // Tworzenie zadania do obsługi Telegrama xTaskCreatePinnedToCore( handleTelegramMessages, // Funkcja zadania "TelegramTask", // Nazwa zadania 8192, // Rozmiar stosu NULL, // Argument dla zadania 1, // Priorytet zadania NULL, // Uchwyt zadania 1 // Rdzeń ESP32 (0 lub 1) ); } void scrollText() { dma_display->fillRect(0, textYPosition, dma_display->width(), 7, myBLACK); textXPosition -= 1; if (textXPosition + text.length() * 6 * FONT_SIZE < 0) { textXPosition = dma_display->width(); } for (int i = 0; i < text.length(); i++) { int charX = textXPosition + i * 6 * FONT_SIZE; if (charX + 6 * FONT_SIZE > 0 && charX < dma_display->width()) { dma_display->setCursor(charX, textYPosition); dma_display->setTextColor(getColorForMode(i)); dma_display->print(text[i]); } } colorOffset += 5; dma_display->flipDMABuffer(); } void loop() { if (millis() - lastScrollTime > scrollSpeed) { lastScrollTime = millis(); drawSymbols(); scrollText(); } }