#include "display/frames.h" #include #include #include "config.h" #include "display/text.h" FlipdotFrames::FlipdotFrames(BROSE9323 &display, PageResources &resources, LifePlayer &life) : _display(display), _resources(resources), _life(life) { } // Only the dots that changed are sent (unless redraw: the Nano's image is unknown) uint32_t FlipdotFrames::dynamicPage(const LocalTime &time, uint32_t now, bool redraw) { uint8_t bitmap[BITMAP_BYTES]; _life.tick(now); uint32_t wait = renderPage(_dynamic, time, now, _resources, bitmap); bool moving = wait != 0; // The Nano has no acknowledgement: the next frame waits until it has received this one // and flipped its dots, or the next frame overflows its serial buffer _useEffect(_page_effect, redraw); uint32_t busy = _show(bitmap, !redraw, moving || _instant_next); _instant_next = false; return moving && wait < busy ? busy : wait; } void FlipdotFrames::setDrive(uint16_t flip_time, const DriveSettings &drive) { _dot_us = (uint32_t)drive.pulses * (flip_time + drive.flip_gap); _drive = drive; _nano_effect = EFFECT_UNKNOWN; } void FlipdotFrames::_useEffect(uint8_t effect, bool full) { if (effect == PAGE_EFFECT_DEFAULT || effect >= DRIVE_EFFECT_COUNT) effect = _drive.effect; if (effect == _nano_effect && !full) return; uint8_t flags = EFFECT_FLAG_NO_SAVE | (_drive.effect_small_only ? EFFECT_FLAG_SMALL_ONLY : 0); _display.setEffect(effect, _drive.effect_time, flags); _nano_effect = effect; } // Sends the bitmap; returns about how long the Nano takes to receive and show it in ms uint32_t FlipdotFrames::_show(const uint8_t *bitmap, bool changes_only, bool instant) { uint32_t dots = 0; for (int y = 0; y < BITMAP_HEIGHT; y++) for (int x = 0; x < BITMAP_WIDTH; x++) dots += bitmapPixel(bitmap, x, y) != _display.shownPixel(x, y); size_t chars = _display.showBitmap(bitmap, changes_only, instant); if (chars == 0) return 0; return chars / NANO_CHARS_PER_MS + dots * _dot_us / 1000 + FRAME_MARGIN; } void FlipdotFrames::noWifi(int hour, int minute) { char time[6]; snprintf(time, sizeof(time), "%02d:%02d", hour, minute); _useEffect(PAGE_EFFECT_DEFAULT, false); drawTextLines(_display, "No WiFi", time); _display.display(); } void FlipdotFrames::page() { _useEffect(_page_effect, true); _show(_page, false, _instant_next); _instant_next = false; } void FlipdotFrames::live() { _drawFullScreen(_live); } void FlipdotFrames::off() { _drawFullScreen(_off); } void FlipdotFrames::setDynamicPage(const DynamicPage &page) { _dynamic = page; _life.setPage(page); } void FlipdotFrames::setPageBitmap(const uint8_t *bitmap) { memcpy(_page, bitmap, BITMAP_BYTES); } void FlipdotFrames::setLiveBitmap(const uint8_t *bitmap) { memcpy(_live, bitmap, BITMAP_BYTES); } void FlipdotFrames::setOffBitmap(const uint8_t *bitmap) { if (bitmap) memcpy(_off, bitmap, BITMAP_BYTES); else memset(_off, 0, BITMAP_BYTES); } void FlipdotFrames::_drawFullScreen(const uint8_t *bitmap) { _useEffect(PAGE_EFFECT_DEFAULT, true); _show(bitmap, false, false); }