class Effect { protected: Window window = {0, 0, LED_WIDTH, LED_HEIGHT}; // Use a full screen window per default. public: virtual void loop() = 0; boolean supports_window = false; virtual boolean can_be_shown_with_clock() { return false; }; virtual boolean clock_as_mask() { return false; }; void setWindow(Window win) { window = win; }; }; struct EffectEntry { char* name; Effect* effect; }; class Bell : public Effect { private: CRGB color_on = CRGB(0xFFFF00); CRGB color_off = CRGB(0x000000); boolean invert = false; public: void loop() { Serial.println("This is Bell.loop()"); for (int y = 0; y < 16; y++) { for (int x = 0; x < 2; x++) { for (int z = 0; z < 8; z++) { leds[XYsafe(x * 8 + z, y)] = sprite_bell[y * 2 + x] >> (7 - z) & 1 ^ invert ? color_on : color_off; } } } EVERY_N_MILLISECONDS(300) { invert = !invert; } } }; class BigClock : public Effect { private: CRGB color_h = CRGB(0xFF0000); CRGB color_m = CRGB(0x00FF00); CRGB color_colon = CRGB(0xFFFF00); void drawNumber(uint8_t number, int x, int y, CRGB color) { char buffer[7]; sprintf(buffer, "%02d", number); drawText(buffer, x, y, color); } void drawText(char *text, int x, int y, CRGB color) { for (int i = 0; i < strlen(text); i++) { drawSprite(font_char(numbers4x7, text[i]), x + i * 4, y, color); } } unsigned char* font_char(unsigned char* font, char c) { return &font[(c - 48) * 4]; } void drawSprite(unsigned char* sprite, int xOffset, int yOffset, CRGB color) { for ( byte y = 0; y < 7; y++) { for ( byte x = 0; x < 4; x++) { bool on = (sprite[x] >> y & 1) * 255; if (on) { leds[ XYsafe(x + xOffset, y + yOffset) ] = color; } } } } public: BigClock() {} void loop() { clear(); drawNumber(ntpClient.getHours(), 0, 0, color_h); drawNumber(ntpClient.getMinutes(), 8, 0, color_m); /*if (ntpClient.getSeconds() & 1) { leds[XYsafe(13, 2)] = color_colon; leds[XYsafe(13, 5)] = color_colon; }*/ drawNumber(ntpClient.getSeconds(), 8, 8, color_colon); } }; class Clock : public Effect { private: Window window = {0, LED_HEIGHT - 6, LED_WIDTH, 6}; public: Clock() {} void loop() { loop(false, CRGB(0xFFFFFF), CRGB(0x000000)); } void loop(boolean invert, CRGB fg_color, CRGB bg_color) { if (!invert) { clear(window, bg_color); } else { // Manually clear the needed parts for(int i=0; icolor_count]; int led_index = 0; uint8_t *color_data = new uint8_t[animation->color_count * 3]; memcpy_P(color_data, animation->colors, animation->color_count * 3); for (int i = 0; i < animation->color_count; i++) colors[i] = CRGB(color_data[i * 3], color_data[i * 3 + 1], color_data[i * 3 + 2]); free(color_data); // Data is stored in progmem, so get it from there. int length = animation->offsets[frame + 1] - animation->offsets[frame]; uint8_t *data = new uint8_t[length]; memcpy_P(data, animation->data + animation->offsets[frame], length); for (int i = 0; i < length; i++) { uint8_t color_index; uint8_t count; if (data[i] == 255) { // Run-length encoded data color_index = data[i + 2]; count = data[i + 1]; i += 2; } else { color_index = data[i]; count = 1; } if (color_index == 0) { // color #0 = skip this pixels led_index += count; } else { CRGB* color; if (color_index == 1) { color = &background_color; } else if (color_index >= 2) { color = &colors[color_index - 2]; } for (int j = 0; j < count; j++) set(led_index++, color); } } free(data); if (frameSince == 0 || frameSince + frameDelay(animation, frame) < millis() || frameSince > millis()) { frame = (frame + 1) % animation->frame_count; frameSince = millis(); } } void set(int i, CRGB* color) { setPixel(xOffset + (i % animation->w), yOffset + (i / animation->h), *color); } uint16_t frameDelay(AnimationData* animation, int frame) { if (animation->individual_delays) return animation->delays[frame]; return animation->delays[0]; } }; class SingleDynamic : public Effect { protected: static const int factor = 2; static const int tile_count = LED_WIDTH/factor * LED_HEIGHT/factor; CRGB tiles[tile_count]; public: SingleDynamic() { for (int i=0; i window->h) running = false; } void draw() { for(int i=0; i speed) { advance(); last_move = millis(); } draw(); } } }; class MatrixEffect : public Effect { private: MatrixColumn columns[LED_WIDTH]; public: boolean can_be_shown_with_clock() { return true; }; MatrixEffect() { for (int i=0; i