mirror of
https://github.com/meshtastic/firmware.git
synced 2025-10-28 15:22:55 +00:00
fix for message time
This commit is contained in:
parent
4e61016a44
commit
6ead5c04bd
@ -4,6 +4,7 @@
|
|||||||
#include "SPILock.h"
|
#include "SPILock.h"
|
||||||
#include "SafeFile.h"
|
#include "SafeFile.h"
|
||||||
#include "configuration.h" // for millis()
|
#include "configuration.h" // for millis()
|
||||||
|
#include "gps/RTC.h"
|
||||||
#include "graphics/draw/MessageRenderer.h"
|
#include "graphics/draw/MessageRenderer.h"
|
||||||
|
|
||||||
using graphics::MessageRenderer::setThreadMode;
|
using graphics::MessageRenderer::setThreadMode;
|
||||||
@ -36,7 +37,16 @@ void MessageStore::addFromPacket(const meshtastic_MeshPacket &packet)
|
|||||||
{
|
{
|
||||||
StoredMessage sm;
|
StoredMessage sm;
|
||||||
|
|
||||||
sm.timestamp = packet.rx_time ? packet.rx_time : (millis() / 1000);
|
// Always use our local time, ignore packet.rx_time
|
||||||
|
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
|
if (nowSecs > 0) {
|
||||||
|
sm.timestamp = nowSecs;
|
||||||
|
sm.isBootRelative = false;
|
||||||
|
} else {
|
||||||
|
sm.timestamp = millis() / 1000;
|
||||||
|
sm.isBootRelative = true; // mark for later upgrade
|
||||||
|
}
|
||||||
|
|
||||||
sm.sender = packet.from;
|
sm.sender = packet.from;
|
||||||
sm.channelIndex = packet.channel;
|
sm.channelIndex = packet.channel;
|
||||||
sm.text = std::string(reinterpret_cast<const char *>(packet.decoded.payload.bytes));
|
sm.text = std::string(reinterpret_cast<const char *>(packet.decoded.payload.bytes));
|
||||||
@ -67,7 +77,17 @@ void MessageStore::addFromPacket(const meshtastic_MeshPacket &packet)
|
|||||||
void MessageStore::addFromString(uint32_t sender, uint8_t channelIndex, const std::string &text)
|
void MessageStore::addFromString(uint32_t sender, uint8_t channelIndex, const std::string &text)
|
||||||
{
|
{
|
||||||
StoredMessage sm;
|
StoredMessage sm;
|
||||||
sm.timestamp = millis() / 1000;
|
|
||||||
|
// Always use our local time
|
||||||
|
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
|
if (nowSecs > 0) {
|
||||||
|
sm.timestamp = nowSecs;
|
||||||
|
sm.isBootRelative = false;
|
||||||
|
} else {
|
||||||
|
sm.timestamp = millis() / 1000;
|
||||||
|
sm.isBootRelative = true; // mark for later upgrade
|
||||||
|
}
|
||||||
|
|
||||||
sm.sender = sender;
|
sm.sender = sender;
|
||||||
sm.channelIndex = channelIndex;
|
sm.channelIndex = channelIndex;
|
||||||
sm.text = text;
|
sm.text = text;
|
||||||
@ -104,6 +124,8 @@ void MessageStore::saveToFlash()
|
|||||||
f.write((uint8_t *)&m.dest, sizeof(m.dest));
|
f.write((uint8_t *)&m.dest, sizeof(m.dest));
|
||||||
f.write((uint8_t *)m.text.c_str(), std::min(static_cast<size_t>(MAX_MESSAGE_SIZE), m.text.size()));
|
f.write((uint8_t *)m.text.c_str(), std::min(static_cast<size_t>(MAX_MESSAGE_SIZE), m.text.size()));
|
||||||
f.write('\0'); // null terminator
|
f.write('\0'); // null terminator
|
||||||
|
uint8_t bootFlag = m.isBootRelative ? 1 : 0;
|
||||||
|
f.write(&bootFlag, 1); // persist boot-relative flag
|
||||||
}
|
}
|
||||||
spiLock->unlock();
|
spiLock->unlock();
|
||||||
|
|
||||||
@ -146,6 +168,20 @@ void MessageStore::loadFromFlash()
|
|||||||
m.text.push_back(c);
|
m.text.push_back(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to read boot-relative flag (new format)
|
||||||
|
uint8_t bootFlag = 0;
|
||||||
|
if (f.available() > 0) {
|
||||||
|
if (f.readBytes((char *)&bootFlag, 1) == 1) {
|
||||||
|
m.isBootRelative = (bootFlag != 0);
|
||||||
|
} else {
|
||||||
|
// Old format, fallback heuristic
|
||||||
|
m.isBootRelative = (m.timestamp < 60u * 60u * 24u * 7u);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Old format, fallback heuristic
|
||||||
|
m.isBootRelative = (m.timestamp < 60u * 60u * 24u * 7u);
|
||||||
|
}
|
||||||
|
|
||||||
// Recompute type from dest
|
// Recompute type from dest
|
||||||
if (m.dest == NODENUM_BROADCAST) {
|
if (m.dest == NODENUM_BROADCAST) {
|
||||||
m.type = MessageType::BROADCAST;
|
m.type = MessageType::BROADCAST;
|
||||||
@ -221,5 +257,35 @@ std::deque<StoredMessage> MessageStore::getDirectMessages() const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === Upgrade boot-relative timestamps once RTC is valid ===
|
||||||
|
// Only same-boot boot-relative messages are healed.
|
||||||
|
// Persisted boot-relative messages from old boots stay ??? forever.
|
||||||
|
void MessageStore::upgradeBootRelativeTimestamps()
|
||||||
|
{
|
||||||
|
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
|
if (nowSecs == 0)
|
||||||
|
return; // Still no valid RTC
|
||||||
|
|
||||||
|
uint32_t bootNow = millis() / 1000;
|
||||||
|
|
||||||
|
for (auto &m : liveMessages) {
|
||||||
|
if (m.isBootRelative && m.timestamp <= bootNow) {
|
||||||
|
uint32_t bootOffset = nowSecs - bootNow;
|
||||||
|
m.timestamp += bootOffset;
|
||||||
|
m.isBootRelative = false;
|
||||||
|
}
|
||||||
|
// else: persisted from old boot → stays ??? forever
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &m : messages) {
|
||||||
|
if (m.isBootRelative && m.timestamp <= bootNow) {
|
||||||
|
uint32_t bootOffset = nowSecs - bootNow;
|
||||||
|
m.timestamp += bootOffset;
|
||||||
|
m.isBootRelative = false;
|
||||||
|
}
|
||||||
|
// else: persisted from old boot → stays ??? forever
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// === Global definition ===
|
// === Global definition ===
|
||||||
MessageStore messageStore("default");
|
MessageStore messageStore("default");
|
||||||
|
|||||||
@ -24,6 +24,17 @@ struct StoredMessage {
|
|||||||
|
|
||||||
// Explicit classification (derived from dest when loading old messages)
|
// Explicit classification (derived from dest when loading old messages)
|
||||||
MessageType type;
|
MessageType type;
|
||||||
|
|
||||||
|
// Marks whether the timestamp was stored relative to boot time
|
||||||
|
// (true = millis()/1000 fallback, false = epoch/RTC absolute)
|
||||||
|
bool isBootRelative;
|
||||||
|
|
||||||
|
// Default constructor to initialize all fields safely
|
||||||
|
StoredMessage()
|
||||||
|
: timestamp(0), sender(0), channelIndex(0), text(""), dest(0xffffffff), type(MessageType::BROADCAST),
|
||||||
|
isBootRelative(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class MessageStore
|
class MessageStore
|
||||||
@ -61,6 +72,9 @@ class MessageStore
|
|||||||
std::deque<StoredMessage> getDirectMessages() const;
|
std::deque<StoredMessage> getDirectMessages() const;
|
||||||
std::deque<StoredMessage> getConversationWith(uint32_t peer) const;
|
std::deque<StoredMessage> getConversationWith(uint32_t peer) const;
|
||||||
|
|
||||||
|
// Upgrade boot-relative timestamps once RTC is valid
|
||||||
|
void upgradeBootRelativeTimestamps();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// RAM buffer (always current, main source for UI)
|
// RAM buffer (always current, main source for UI)
|
||||||
std::deque<StoredMessage> liveMessages;
|
std::deque<StoredMessage> liveMessages;
|
||||||
|
|||||||
@ -338,9 +338,8 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
|||||||
uint8_t TFT_MESH_b = rawRGB & 0xFF;
|
uint8_t TFT_MESH_b = rawRGB & 0xFF;
|
||||||
LOG_INFO("Values of r,g,b: %d, %d, %d", TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
|
LOG_INFO("Values of r,g,b: %d, %d, %d", TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
|
||||||
|
|
||||||
if (TFT_MESH_r <= 255 && TFT_MESH_g <= 255 && TFT_MESH_b <= 255) {
|
// Values are always 0–255, no need to check
|
||||||
TFT_MESH = COLOR565(TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
|
TFT_MESH = COLOR565(TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
|
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
|
||||||
@ -801,6 +800,7 @@ int32_t Screen::runOnce()
|
|||||||
break;
|
break;
|
||||||
case Cmd::STOP_ALERT_FRAME:
|
case Cmd::STOP_ALERT_FRAME:
|
||||||
NotificationRenderer::pauseBanner = false;
|
NotificationRenderer::pauseBanner = false;
|
||||||
|
break;
|
||||||
case Cmd::STOP_BOOT_SCREEN:
|
case Cmd::STOP_BOOT_SCREEN:
|
||||||
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
|
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
|
||||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||||
@ -971,9 +971,6 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Declare this early so it’s available in FOCUS_PRESERVE block
|
|
||||||
bool willInsertTextMessage = shouldDrawMessage(&devicestate.rx_text_message);
|
|
||||||
|
|
||||||
if (!hiddenFrames.home) {
|
if (!hiddenFrames.home) {
|
||||||
fsi.positions.home = numframes;
|
fsi.positions.home = numframes;
|
||||||
normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused;
|
normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused;
|
||||||
@ -1441,7 +1438,17 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
|
|
||||||
// === Save our own outgoing message to live RAM ===
|
// === Save our own outgoing message to live RAM ===
|
||||||
StoredMessage sm;
|
StoredMessage sm;
|
||||||
sm.timestamp = millis() / 1000;
|
|
||||||
|
// Always use our local time
|
||||||
|
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
|
if (nowSecs > 0) {
|
||||||
|
sm.timestamp = nowSecs;
|
||||||
|
sm.isBootRelative = false;
|
||||||
|
} else {
|
||||||
|
sm.timestamp = millis() / 1000;
|
||||||
|
sm.isBootRelative = true; // mark for later upgrade
|
||||||
|
}
|
||||||
|
|
||||||
sm.sender = nodeDB->getNodeNum(); // us
|
sm.sender = nodeDB->getNodeNum(); // us
|
||||||
sm.channelIndex = packet->channel;
|
sm.channelIndex = packet->channel;
|
||||||
sm.text = std::string(reinterpret_cast<const char *>(packet->decoded.payload.bytes));
|
sm.text = std::string(reinterpret_cast<const char *>(packet->decoded.payload.bytes));
|
||||||
@ -1524,7 +1531,17 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
|||||||
|
|
||||||
// === Save this incoming message to live RAM ===
|
// === Save this incoming message to live RAM ===
|
||||||
StoredMessage sm;
|
StoredMessage sm;
|
||||||
sm.timestamp = packet->rx_time ? packet->rx_time : (millis() / 1000);
|
|
||||||
|
// Always use our local time
|
||||||
|
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
|
if (nowSecs > 0) {
|
||||||
|
sm.timestamp = nowSecs;
|
||||||
|
sm.isBootRelative = false;
|
||||||
|
} else {
|
||||||
|
sm.timestamp = millis() / 1000;
|
||||||
|
sm.isBootRelative = true; // mark for later upgrade
|
||||||
|
}
|
||||||
|
|
||||||
sm.sender = packet->from;
|
sm.sender = packet->from;
|
||||||
sm.channelIndex = packet->channel;
|
sm.channelIndex = packet->channel;
|
||||||
sm.text = std::string(reinterpret_cast<const char *>(packet->decoded.payload.bytes));
|
sm.text = std::string(reinterpret_cast<const char *>(packet->decoded.payload.bytes));
|
||||||
|
|||||||
@ -57,8 +57,6 @@ namespace graphics
|
|||||||
namespace MessageRenderer
|
namespace MessageRenderer
|
||||||
{
|
{
|
||||||
|
|
||||||
// Simple cache based on text hash
|
|
||||||
static size_t cachedKey = 0;
|
|
||||||
static std::vector<std::string> cachedLines;
|
static std::vector<std::string> cachedLines;
|
||||||
static std::vector<int> cachedHeights;
|
static std::vector<int> cachedHeights;
|
||||||
|
|
||||||
@ -249,6 +247,9 @@ const std::vector<uint32_t> &getSeenPeers()
|
|||||||
|
|
||||||
void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
|
// Ensure any boot-relative timestamps are upgraded if RTC is valid
|
||||||
|
messageStore.upgradeBootRelativeTimestamps();
|
||||||
|
|
||||||
if (!didReset) {
|
if (!didReset) {
|
||||||
resetScrollState();
|
resetScrollState();
|
||||||
didReset = true;
|
didReset = true;
|
||||||
@ -375,12 +376,33 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
snprintf(chanType, sizeof(chanType), "(DM)");
|
snprintf(chanType, sizeof(chanType), "(DM)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else: leave empty for thread views
|
|
||||||
|
|
||||||
// Calculate how long ago
|
// Calculate how long ago
|
||||||
uint32_t nowSecs = millis() / 1000;
|
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
uint32_t seconds = (nowSecs > m.timestamp) ? (nowSecs - m.timestamp) : 0;
|
uint32_t seconds = 0;
|
||||||
bool invalidTime = (m.timestamp == 0 || seconds > 315360000); // >10 years
|
bool invalidTime = true;
|
||||||
|
|
||||||
|
if (m.timestamp > 0 && nowSecs > 0) {
|
||||||
|
if (nowSecs >= m.timestamp) {
|
||||||
|
seconds = nowSecs - m.timestamp;
|
||||||
|
invalidTime = (seconds > 315360000); // >10 years
|
||||||
|
} else {
|
||||||
|
uint32_t ahead = m.timestamp - nowSecs;
|
||||||
|
if (ahead <= 600) { // allow small skew
|
||||||
|
seconds = 0;
|
||||||
|
invalidTime = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (m.timestamp > 0 && nowSecs == 0) {
|
||||||
|
// RTC not valid: only trust boot-relative if same boot
|
||||||
|
uint32_t bootNow = millis() / 1000;
|
||||||
|
if (m.isBootRelative && m.timestamp <= bootNow) {
|
||||||
|
seconds = bootNow - m.timestamp;
|
||||||
|
invalidTime = false;
|
||||||
|
} else {
|
||||||
|
invalidTime = true; // old persisted boot-relative, ignore until healed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
char timeBuf[16];
|
char timeBuf[16];
|
||||||
if (invalidTime) {
|
if (invalidTime) {
|
||||||
@ -492,7 +514,6 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw screen title header last
|
|
||||||
graphics::drawCommonHeader(display, x, y, titleStr);
|
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include "SPILock.h"
|
#include "SPILock.h"
|
||||||
#include "buzz.h"
|
#include "buzz.h"
|
||||||
#include "detect/ScanI2C.h"
|
#include "detect/ScanI2C.h"
|
||||||
|
#include "gps/RTC.h"
|
||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
#include "graphics/SharedUIDisplay.h"
|
#include "graphics/SharedUIDisplay.h"
|
||||||
#include "graphics/draw/MessageRenderer.h"
|
#include "graphics/draw/MessageRenderer.h"
|
||||||
@ -974,7 +975,17 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
|
|||||||
|
|
||||||
// Save outgoing message
|
// Save outgoing message
|
||||||
StoredMessage sm;
|
StoredMessage sm;
|
||||||
sm.timestamp = millis() / 1000;
|
|
||||||
|
// Always use our local time, consistent with other paths
|
||||||
|
uint32_t nowSecs = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||||
|
if (nowSecs > 0) {
|
||||||
|
sm.timestamp = nowSecs;
|
||||||
|
sm.isBootRelative = false;
|
||||||
|
} else {
|
||||||
|
sm.timestamp = millis() / 1000;
|
||||||
|
sm.isBootRelative = true; // mark for later upgrade
|
||||||
|
}
|
||||||
|
|
||||||
sm.sender = nodeDB->getNodeNum(); // us
|
sm.sender = nodeDB->getNodeNum(); // us
|
||||||
sm.channelIndex = channel;
|
sm.channelIndex = channel;
|
||||||
sm.text = std::string(message);
|
sm.text = std::string(message);
|
||||||
|
|||||||
@ -3,7 +3,13 @@
|
|||||||
#include "SinglePortModule.h"
|
#include "SinglePortModule.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text message handling for meshtastic - draws on the OLED display the most recent received message
|
* Text message handling for Meshtastic.
|
||||||
|
*
|
||||||
|
* This module is responsible for receiving and storing incoming text messages
|
||||||
|
* from the mesh. It updates device state and notifies observers so that other
|
||||||
|
* components (such as the MessageRenderer) can later display or process them.
|
||||||
|
*
|
||||||
|
* Rendering of messages on screen is no longer done here.
|
||||||
*/
|
*/
|
||||||
class TextMessageModule : public SinglePortModule, public Observable<const meshtastic_MeshPacket *>
|
class TextMessageModule : public SinglePortModule, public Observable<const meshtastic_MeshPacket *>
|
||||||
{
|
{
|
||||||
@ -15,12 +21,12 @@ class TextMessageModule : public SinglePortModule, public Observable<const mesht
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** Called to handle a particular incoming message
|
/** Called to handle a particular incoming message
|
||||||
|
*
|
||||||
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
|
* @return ProcessMessage::STOP if you've guaranteed you've handled this
|
||||||
it
|
* message and no other handlers should be considered for it.
|
||||||
*/
|
*/
|
||||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||||
virtual bool wantPacket(const meshtastic_MeshPacket *p) override;
|
virtual bool wantPacket(const meshtastic_MeshPacket *p) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern TextMessageModule *textMessageModule;
|
extern TextMessageModule *textMessageModule;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user