Public Access
Refresh effect per page
Pages choose their own refresh effect (or the Hardware setting), used when the page appears and for its own changes. Schedule blocks that switch instantly and rules still switch instantly. The ESP32 sends page effects to the Nano with a new no-save flag (E ...,2), so page switches don't wear the Nano's EEPROM. Page format 7 (version 6 pages are converted). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a6b946e17c
commit
a904615c51
29 files changed
+152
-17
No files matched your search
@@ -18,6 +18,7 @@ uint32_t FlipdotFrames::dynamicPage(const LocalTime &time, uint32_t now, bool re
|
||||
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;
|
||||
@@ -26,6 +27,19 @@ uint32_t FlipdotFrames::dynamicPage(const LocalTime &time, uint32_t now, bool re
|
||||
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
|
||||
@@ -45,12 +59,14 @@ 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;
|
||||
}
|
||||
@@ -90,5 +106,6 @@ void FlipdotFrames::setOffBitmap(const uint8_t *bitmap)
|
||||
|
||||
void FlipdotFrames::_drawFullScreen(const uint8_t *bitmap)
|
||||
{
|
||||
_useEffect(PAGE_EFFECT_DEFAULT, true);
|
||||
_show(bitmap, false, false);
|
||||
}
|
||||
@@ -29,8 +29,15 @@ public:
|
||||
// The next page frame is shown without the refresh effect (a rule's page, a schedule
|
||||
// block that switches instantly)
|
||||
void showNextInstant() { _instant_next = true; }
|
||||
// How the Nano flips the dots: for the time a frame takes
|
||||
// How the Nano flips the dots: for the time a frame takes, and the refresh effect
|
||||
void setDrive(uint16_t flip_time, const DriveSettings &drive);
|
||||
// The refresh effect of the shown page (PAGE_EFFECT_DEFAULT: the one of the drive
|
||||
// settings). Sent to the Nano before page frames, without storing it in its EEPROM;
|
||||
// the other screens (off, live, No WiFi) use the drive settings' effect.
|
||||
void setPageEffect(uint8_t effect) { _page_effect = effect; }
|
||||
// The Nano got the stored drive settings again (StatusScreen): its effect is the
|
||||
// default one now
|
||||
void driveSent() { _nano_effect = EFFECT_UNKNOWN; }
|
||||
void setPageBitmap(const uint8_t *bitmap);
|
||||
void setLiveBitmap(const uint8_t *bitmap);
|
||||
// nullptr: blank
|
||||
@@ -40,6 +47,10 @@ private:
|
||||
BROSE9323 &_display;
|
||||
PageResources &_resources;
|
||||
DynamicPage _dynamic;
|
||||
static const uint8_t EFFECT_UNKNOWN = 254;
|
||||
DriveSettings _drive;
|
||||
uint8_t _page_effect = PAGE_EFFECT_DEFAULT;
|
||||
uint8_t _nano_effect = EFFECT_UNKNOWN; // the effect last sent by _useEffect()
|
||||
uint32_t _dot_us = 1000;
|
||||
bool _instant_next = false; // per changed dot: pulses x (flip time + gap)
|
||||
uint8_t _page[BITMAP_BYTES] = {};
|
||||
@@ -47,5 +58,8 @@ private:
|
||||
uint8_t _off[BITMAP_BYTES] = {}; // all zero: blank
|
||||
|
||||
void _drawFullScreen(const uint8_t *bitmap);
|
||||
// Makes the Nano use this effect (PAGE_EFFECT_DEFAULT: the drive settings' one). Sent
|
||||
// when it changed, and always with full frames: a restarted Nano has the stored one.
|
||||
void _useEffect(uint8_t effect, bool full);
|
||||
uint32_t _show(const uint8_t *bitmap, bool changes_only, bool instant);
|
||||
};
|
||||
@@ -8,7 +8,7 @@
|
||||
static const char *DIR = "/pages";
|
||||
static const char *EXTENSION = ".pg";
|
||||
// Increase when DynamicPage changes; older files are ignored unless converted below
|
||||
static const uint16_t VERSION = 6;
|
||||
static const uint16_t VERSION = 7;
|
||||
// Elements per page up to version 5 (the old formats below are frozen with it)
|
||||
static const int OLD_PAGE_ELEMENTS = 8;
|
||||
|
||||
@@ -356,9 +356,40 @@ static void convert(const Page &old, DynamicPage &page)
|
||||
}
|
||||
} // namespace v5
|
||||
|
||||
// Version 6 (before the refresh effect per page): the current layout without effect.
|
||||
// Frozen copy; the elements did not change since, so they are copied as a block.
|
||||
namespace v6
|
||||
{
|
||||
struct Page
|
||||
{
|
||||
uint8_t slot_count;
|
||||
PageSlot slots[PAGE_SLOTS];
|
||||
uint8_t element_count;
|
||||
PageElement elements[PAGE_ELEMENTS];
|
||||
};
|
||||
|
||||
struct Stored
|
||||
{
|
||||
uint16_t version;
|
||||
uint16_t size;
|
||||
Page page;
|
||||
};
|
||||
|
||||
static_assert(offsetof(DynamicPage, elements) == offsetof(Page, elements), "same layout up to the elements");
|
||||
|
||||
static void convert(const Page &old, DynamicPage &page)
|
||||
{
|
||||
clearPage(page); // the default effect
|
||||
page.slot_count = old.slot_count;
|
||||
memcpy(page.slots, old.slots, sizeof(page.slots));
|
||||
page.element_count = old.element_count;
|
||||
memcpy((void *)page.elements, old.elements, sizeof(page.elements));
|
||||
}
|
||||
} // namespace v6
|
||||
|
||||
static_assert(sizeof(v1::Stored) != sizeof(v2::Stored) && sizeof(v2::Stored) != sizeof(v3::Stored) &&
|
||||
sizeof(v3::Stored) != sizeof(v4::Stored) && sizeof(v4::Stored) != sizeof(v5::Stored) &&
|
||||
sizeof(v5::Stored) != sizeof(StoredPage),
|
||||
sizeof(v5::Stored) != sizeof(v6::Stored) && sizeof(v6::Stored) != sizeof(StoredPage),
|
||||
"the versions are told apart by their size");
|
||||
|
||||
static uint32_t revision = 0;
|
||||
@@ -429,6 +460,18 @@ bool loadPage(const char *name, DynamicPage &page)
|
||||
page = stored->page;
|
||||
delete stored;
|
||||
}
|
||||
else if (size == sizeof(v6::Stored))
|
||||
{
|
||||
v6::Stored *stored = new v6::Stored;
|
||||
ok = file.read((uint8_t *)stored, sizeof(v6::Stored)) == sizeof(v6::Stored) && stored->version == 6 &&
|
||||
stored->size == sizeof(v6::Page);
|
||||
if (ok)
|
||||
{
|
||||
v6::convert(stored->page, page);
|
||||
ok = validatePage(page) == nullptr;
|
||||
}
|
||||
delete stored;
|
||||
}
|
||||
else if (size == sizeof(v5::Stored))
|
||||
{
|
||||
v5::Stored *stored = new v5::Stored;
|
||||
|
||||
@@ -22,16 +22,17 @@ void StatusScreen::sendDrive()
|
||||
_drive_pending = true;
|
||||
}
|
||||
|
||||
void StatusScreen::loop()
|
||||
bool StatusScreen::loop()
|
||||
{
|
||||
if (!_drive_pending || !_started || !_nano.isOn())
|
||||
return;
|
||||
return false;
|
||||
_drive_pending = false;
|
||||
_display.setTiming(_flip_time);
|
||||
_display.setGap(_drive.flip_gap);
|
||||
_display.setPulses(_drive.pulses);
|
||||
_display.setEffect(_drive.effect, _drive.effect_time, _drive.effect_small_only ? EFFECT_FLAG_SMALL_ONLY : 0);
|
||||
_display.setRefreshInterval(_dot_refresh);
|
||||
return true;
|
||||
}
|
||||
|
||||
void StatusScreen::show(const char *line1, const char *line2, bool keep_on)
|
||||
|
||||
@@ -21,7 +21,8 @@ public:
|
||||
// Send them again (they changed), from loop()
|
||||
void sendDrive();
|
||||
// Sends changed drive settings once the display is on. Not while the Nano is flashed.
|
||||
void loop();
|
||||
// True when they were sent.
|
||||
bool loop();
|
||||
|
||||
private:
|
||||
BROSE9323 &_display;
|
||||
|
||||
Reference in new issue
Block a user