mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-25 17:42:48 +00:00
Screen changes from time deltas to timestamps after 15 minutes (#3935)
* E-Ink displays sometimes use timestamp instead of delta * Allow users to disable E-Ink screensaver * Clarify variable's purpose * Operator order oopsie * Picky print problem * Implement for all display, not just E-Ink * Align "unknown age" behavior with existing code * One more use of timestamp, if screen is wide enough
This commit is contained in:
parent
cdf86f4166
commit
a12b9922ed
@ -348,12 +348,18 @@ void PowerFSM_setup()
|
||||
|
||||
powerFSM.add_transition(&stateDARK, &stateDARK, EVENT_CONTACT_FROM_PHONE, NULL, "Contact from phone");
|
||||
|
||||
powerFSM.add_timed_transition(&stateON, &stateDARK,
|
||||
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
|
||||
"Screen-on timeout");
|
||||
powerFSM.add_timed_transition(&statePOWER, &stateDARK,
|
||||
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
|
||||
"Screen-on timeout");
|
||||
#ifdef USE_EINK
|
||||
// Allow E-Ink devices to suppress the screensaver, if screen timeout set to 0
|
||||
if (config.display.screen_on_secs > 0)
|
||||
#endif
|
||||
{
|
||||
powerFSM.add_timed_transition(&stateON, &stateDARK,
|
||||
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
|
||||
NULL, "Screen-on timeout");
|
||||
powerFSM.add_timed_transition(&statePOWER, &stateDARK,
|
||||
Default::getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs),
|
||||
NULL, "Screen-on timeout");
|
||||
}
|
||||
|
||||
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
|
||||
#ifdef ARCH_ESP32
|
||||
|
@ -419,6 +419,76 @@ static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
|
||||
return packet->from != 0 && !moduleConfig.store_forward.enabled;
|
||||
}
|
||||
|
||||
// Get an absolute time from "seconds ago" info. Returns false if no valid timestamp possible
|
||||
bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int32_t *daysAgo)
|
||||
{
|
||||
// Cache the result - avoid frequent recalculation
|
||||
static uint8_t hoursCached = 0, minutesCached = 0;
|
||||
static uint32_t daysAgoCached = 0;
|
||||
static uint32_t secondsAgoCached = 0;
|
||||
static bool validCached = false;
|
||||
|
||||
// Abort: if timezone not set
|
||||
if (strlen(config.device.tzdef) == 0) {
|
||||
validCached = false;
|
||||
return validCached;
|
||||
}
|
||||
|
||||
// Abort: if invalid pointers passed
|
||||
if (hours == nullptr || minutes == nullptr || daysAgo == nullptr) {
|
||||
validCached = false;
|
||||
return validCached;
|
||||
}
|
||||
|
||||
// Abort: if time seems invalid.. (> 6 months ago, probably seen before RTC set)
|
||||
if (secondsAgo > SEC_PER_DAY * 30UL * 6) {
|
||||
validCached = false;
|
||||
return validCached;
|
||||
}
|
||||
|
||||
// If repeated request, don't bother recalculating
|
||||
if (secondsAgo - secondsAgoCached < 60 && secondsAgoCached != 0) {
|
||||
if (validCached) {
|
||||
*hours = hoursCached;
|
||||
*minutes = minutesCached;
|
||||
*daysAgo = daysAgoCached;
|
||||
}
|
||||
return validCached;
|
||||
}
|
||||
|
||||
// Get local time
|
||||
uint32_t secondsRTC = getValidTime(RTCQuality::RTCQualityDevice, true); // Get local time
|
||||
|
||||
// Abort: if RTC not set
|
||||
if (!secondsRTC) {
|
||||
validCached = false;
|
||||
return validCached;
|
||||
}
|
||||
|
||||
// Get absolute time when last seen
|
||||
uint32_t secondsSeenAt = secondsRTC - secondsAgo;
|
||||
|
||||
// Calculate daysAgo
|
||||
*daysAgo = (secondsRTC / SEC_PER_DAY) - (secondsSeenAt / SEC_PER_DAY); // How many "midnights" have passed
|
||||
|
||||
// Get seconds since midnight
|
||||
uint32_t hms = (secondsRTC - secondsAgo) % SEC_PER_DAY;
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
|
||||
// Tear apart hms into hours and minutes
|
||||
*hours = hms / SEC_PER_HOUR;
|
||||
*minutes = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
|
||||
// Cache the result
|
||||
daysAgoCached = *daysAgo;
|
||||
hoursCached = *hours;
|
||||
minutesCached = *minutes;
|
||||
secondsAgoCached = secondsAgo;
|
||||
|
||||
validCached = true;
|
||||
return validCached;
|
||||
}
|
||||
|
||||
/// Draw the last text message we received
|
||||
static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@ -440,18 +510,36 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
display->setColor(BLACK);
|
||||
}
|
||||
|
||||
// For time delta
|
||||
uint32_t seconds = sinceReceived(&mp);
|
||||
uint32_t minutes = seconds / 60;
|
||||
uint32_t hours = minutes / 60;
|
||||
uint32_t days = hours / 24;
|
||||
|
||||
if (config.display.heading_bold) {
|
||||
display->drawStringf(1 + x, 0 + y, tempBuf, "%s ago from %s",
|
||||
screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
||||
(node && node->has_user) ? node->user.short_name : "???");
|
||||
// For timestamp
|
||||
uint8_t timestampHours, timestampMinutes;
|
||||
int32_t daysAgo;
|
||||
bool useTimestamp = deltaToTimestamp(seconds, ×tampHours, ×tampMinutes, &daysAgo);
|
||||
|
||||
// If bold, draw twice, shifting right by one pixel
|
||||
for (uint8_t xOff = 0; xOff <= (config.display.heading_bold ? 1 : 0); xOff++) {
|
||||
// Show a timestamp if received today, but longer than 15 minutes ago
|
||||
if (useTimestamp && minutes >= 15 && daysAgo == 0) {
|
||||
display->drawStringf(xOff + x, 0 + y, tempBuf, "At %02hu:%02hu from %s", timestampHours, timestampMinutes,
|
||||
(node && node->has_user) ? node->user.short_name : "???");
|
||||
}
|
||||
// Timestamp yesterday (if display is wide enough)
|
||||
else if (useTimestamp && daysAgo == 1 && display->width() >= 200) {
|
||||
display->drawStringf(xOff + x, 0 + y, tempBuf, "Yesterday %02hu:%02hu from %s", timestampHours, timestampMinutes,
|
||||
(node && node->has_user) ? node->user.short_name : "???");
|
||||
}
|
||||
// Otherwise, show a time delta
|
||||
else {
|
||||
display->drawStringf(xOff + x, 0 + y, tempBuf, "%s ago from %s",
|
||||
screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
||||
(node && node->has_user) ? node->user.short_name : "???");
|
||||
}
|
||||
}
|
||||
display->drawStringf(0 + x, 0 + y, tempBuf, "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
||||
(node && node->has_user) ? node->user.short_name : "???");
|
||||
|
||||
display->setColor(WHITE);
|
||||
snprintf(tempBuf, sizeof(tempBuf), "%s", mp.decoded.payload.bytes);
|
||||
@ -879,19 +967,32 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
|
||||
uint32_t agoSecs = sinceLastSeen(node);
|
||||
static char lastStr[20];
|
||||
|
||||
// Use an absolute timestamp in some cases.
|
||||
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
|
||||
uint8_t timestampHours, timestampMinutes;
|
||||
int32_t daysAgo;
|
||||
bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo);
|
||||
|
||||
if (agoSecs < 120) // last 2 mins?
|
||||
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
|
||||
// -- if suitable for timestamp --
|
||||
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
|
||||
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
|
||||
else if (useTimestamp && daysAgo == 0) // Today
|
||||
snprintf(lastStr, sizeof(lastStr), "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
|
||||
else if (useTimestamp && daysAgo == 1) // Yesterday
|
||||
snprintf(lastStr, sizeof(lastStr), "Seen yesterday");
|
||||
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
|
||||
snprintf(lastStr, sizeof(lastStr), "%li days ago", (long)daysAgo);
|
||||
// -- if using time delta instead --
|
||||
else if (agoSecs < 120 * 60) // last 2 hrs
|
||||
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
|
||||
else {
|
||||
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad
|
||||
// data.
|
||||
if ((agoSecs / 60 / 60) < (hours_in_month * 6)) {
|
||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
||||
} else {
|
||||
snprintf(lastStr, sizeof(lastStr), "unknown age");
|
||||
}
|
||||
}
|
||||
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
|
||||
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
|
||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
||||
else
|
||||
snprintf(lastStr, sizeof(lastStr), "unknown age");
|
||||
|
||||
static char distStr[20];
|
||||
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
||||
@ -900,7 +1001,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
strncpy(distStr, "? km", sizeof(distStr));
|
||||
}
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
const char *fields[] = {username, distStr, signalStr, lastStr, NULL};
|
||||
const char *fields[] = {username, lastStr, signalStr, distStr, NULL};
|
||||
int16_t compassX = 0, compassY = 0;
|
||||
|
||||
// coordinates for the center of the compass/circle
|
||||
@ -1448,6 +1549,15 @@ void Screen::setFrames()
|
||||
LOG_DEBUG("showing standard frames\n");
|
||||
showingNormalScreen = true;
|
||||
|
||||
#ifdef USE_EINK
|
||||
// If user has disabled the screensaver, warn them after boot
|
||||
static bool warnedScreensaverDisabled = false;
|
||||
if (config.display.screen_on_secs == 0 && !warnedScreensaverDisabled) {
|
||||
screen->print("Screensaver disabled\n");
|
||||
warnedScreensaverDisabled = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
moduleFrames = MeshModule::GetMeshModulesWithUIFrames();
|
||||
LOG_DEBUG("Showing %d module frames\n", moduleFrames.size());
|
||||
#ifdef DEBUG_PORT
|
||||
|
Loading…
Reference in New Issue
Block a user