mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-13 22:32:27 +00:00
Compare commits
1 Commits
81799af73d
...
flat-noded
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e85cddef94 |
@@ -53,8 +53,8 @@ class GPSStatus : public Status
|
||||
int32_t getLatitude() const
|
||||
{
|
||||
if (config.position.fixed_position) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
return node->position.latitude_i;
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
return node ? node->latitude_i : 0;
|
||||
} else {
|
||||
return p.latitude_i;
|
||||
}
|
||||
@@ -63,8 +63,8 @@ class GPSStatus : public Status
|
||||
int32_t getLongitude() const
|
||||
{
|
||||
if (config.position.fixed_position) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
return node->position.longitude_i;
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
return node ? node->longitude_i : 0;
|
||||
} else {
|
||||
return p.longitude_i;
|
||||
}
|
||||
@@ -73,8 +73,8 @@ class GPSStatus : public Status
|
||||
int32_t getAltitude() const
|
||||
{
|
||||
if (config.position.fixed_position) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
return node->position.altitude;
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
return node ? node->altitude : 0;
|
||||
} else {
|
||||
return p.altitude;
|
||||
}
|
||||
|
||||
@@ -1106,8 +1106,8 @@ void Screen::setFrames(FrameFocus focus)
|
||||
std::vector<FrameCallback> favoriteFrames;
|
||||
|
||||
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||
const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
|
||||
if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) {
|
||||
const meshtastic_NodeDetail *nodeDetail = nodeDB->getMeshNodeByIndex(i);
|
||||
if (nodeDetail && nodeDetail->num != nodeDB->getNodeNum() && detailIsFavorite(*nodeDetail)) {
|
||||
favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo);
|
||||
}
|
||||
}
|
||||
@@ -1460,10 +1460,10 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
forceDisplay(); // Forces screen redraw
|
||||
}
|
||||
// === Prepare banner content ===
|
||||
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet->from);
|
||||
const meshtastic_NodeDetail *node = nodeDB->getMeshNode(packet->from);
|
||||
const meshtastic_Channel channel =
|
||||
channels.getByIndex(packet->channel ? packet->channel : channels.getPrimaryIndex());
|
||||
const char *longName = (node && node->has_user) ? node->user.long_name : nullptr;
|
||||
const char *longName = (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) ? node->long_name : nullptr;
|
||||
|
||||
const char *msgRaw = reinterpret_cast<const char *>(packet->decoded.payload.bytes);
|
||||
|
||||
|
||||
@@ -1191,8 +1191,8 @@ void menuHandler::removeFavoriteMenu()
|
||||
BannerOverlayOptions bannerOptions;
|
||||
std::string message = "Unfavorite This Node?\n";
|
||||
auto node = nodeDB->getMeshNode(graphics::UIRenderer::currentFavoriteNodeNum);
|
||||
if (node && node->has_user) {
|
||||
message += sanitizeString(node->user.long_name).substr(0, 15);
|
||||
if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
message += sanitizeString(node->long_name).substr(0, 15);
|
||||
}
|
||||
bannerOptions.message = message.c_str();
|
||||
bannerOptions.optionsArrayPtr = optionsArray;
|
||||
@@ -1459,8 +1459,10 @@ void menuHandler::keyVerificationFinalPrompt()
|
||||
options.notificationType = graphics::notificationTypeEnum::selection_picker;
|
||||
options.bannerCallback = [=](int selected) {
|
||||
if (selected == 1) {
|
||||
auto remoteNodePtr = nodeDB->getMeshNode(keyVerificationModule->getCurrentRemoteNode());
|
||||
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
meshtastic_NodeDetail *remoteNodePtr = nodeDB->getMeshNode(keyVerificationModule->getCurrentRemoteNode());
|
||||
if (remoteNodePtr) {
|
||||
detailSetFlag(*remoteNodePtr, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(options);
|
||||
|
||||
@@ -218,18 +218,18 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
}
|
||||
|
||||
// === Header Construction ===
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
|
||||
const meshtastic_NodeDetail *node = nodeDB->getMeshNode(getFrom(&mp));
|
||||
char headerStr[80];
|
||||
const char *sender = "???";
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
if (node && node->has_user)
|
||||
sender = node->user.short_name;
|
||||
if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER))
|
||||
sender = node->short_name;
|
||||
#else
|
||||
if (node && node->has_user) {
|
||||
if (SCREEN_WIDTH >= 200 && strlen(node->user.long_name) > 0) {
|
||||
sender = node->user.long_name;
|
||||
if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
if (SCREEN_WIDTH >= 200 && node->long_name[0] != '\0') {
|
||||
sender = node->long_name;
|
||||
} else {
|
||||
sender = node->user.short_name;
|
||||
sender = node->short_name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -53,23 +53,29 @@ static int scrollIndex = 0;
|
||||
// Utility Functions
|
||||
// =============================
|
||||
|
||||
const char *getSafeNodeName(OLEDDisplay *display, meshtastic_NodeInfoLite *node)
|
||||
const char *getSafeNodeName(OLEDDisplay *display, const meshtastic_NodeDetail *node)
|
||||
{
|
||||
const char *name = NULL;
|
||||
static char nodeName[16] = "?";
|
||||
if (config.display.use_long_node_name == true) {
|
||||
if (node->has_user && strlen(node->user.long_name) > 0) {
|
||||
name = node->user.long_name;
|
||||
} else {
|
||||
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
|
||||
if (!node) {
|
||||
strncpy(nodeName, "?", sizeof(nodeName));
|
||||
return nodeName;
|
||||
}
|
||||
|
||||
const bool hasUser = detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER);
|
||||
const char *name = nullptr;
|
||||
if (config.display.use_long_node_name) {
|
||||
if (hasUser && node->long_name[0] != '\0') {
|
||||
name = node->long_name;
|
||||
}
|
||||
} else {
|
||||
if (node->has_user && strlen(node->user.short_name) > 0) {
|
||||
name = node->user.short_name;
|
||||
} else {
|
||||
snprintf(nodeName, sizeof(nodeName), "(%04X)", (uint16_t)(node->num & 0xFFFF));
|
||||
if (hasUser && node->short_name[0] != '\0') {
|
||||
name = node->short_name;
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
snprintf(nodeName, sizeof(nodeName), "(%04X)", static_cast<uint16_t>(node->num & 0xFFFF));
|
||||
name = nodeName;
|
||||
}
|
||||
|
||||
// Use sanitizeString() function and copy directly into nodeName
|
||||
std::string sanitized_name = sanitizeString(name ? name : "");
|
||||
@@ -164,7 +170,7 @@ void drawScrollbar(OLEDDisplay *display, int visibleNodeRows, int totalEntries,
|
||||
// Entry Renderers
|
||||
// =============================
|
||||
|
||||
void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
|
||||
void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth)
|
||||
{
|
||||
bool isLeftCol = (x < SCREEN_WIDTH / 2);
|
||||
int timeOffset = (isHighResolution) ? (isLeftCol ? 7 : 10) : (isLeftCol ? 3 : 7);
|
||||
@@ -189,7 +195,7 @@ void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(x + ((isHighResolution) ? 6 : 3), y, nodeName);
|
||||
if (node->is_favorite) {
|
||||
if (node && detailIsFavorite(*node)) {
|
||||
if (isHighResolution) {
|
||||
drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
|
||||
} else {
|
||||
@@ -204,7 +210,7 @@ void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
|
||||
display->drawString(rightEdge - textWidth, y, timeStr);
|
||||
}
|
||||
|
||||
void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
|
||||
void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth)
|
||||
{
|
||||
bool isLeftCol = (x < SCREEN_WIDTH / 2);
|
||||
|
||||
@@ -220,7 +226,7 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
|
||||
display->setFont(FONT_SMALL);
|
||||
|
||||
display->drawStringMaxWidth(x + ((isHighResolution) ? 6 : 3), y, nameMaxWidth, nodeName);
|
||||
if (node->is_favorite) {
|
||||
if (node && detailIsFavorite(*node)) {
|
||||
if (isHighResolution) {
|
||||
drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
|
||||
} else {
|
||||
@@ -243,7 +249,7 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
|
||||
|
||||
// Draw hop count
|
||||
char hopStr[6] = "";
|
||||
if (node->has_hops_away && node->hops_away > 0)
|
||||
if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY) && node->hops_away > 0)
|
||||
snprintf(hopStr, sizeof(hopStr), "[%d]", node->hops_away);
|
||||
|
||||
if (hopStr[0] != '\0') {
|
||||
@@ -253,7 +259,7 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
|
||||
}
|
||||
}
|
||||
|
||||
void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
|
||||
void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth)
|
||||
{
|
||||
bool isLeftCol = (x < SCREEN_WIDTH / 2);
|
||||
int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22));
|
||||
@@ -261,12 +267,12 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
const char *nodeName = getSafeNodeName(display, node);
|
||||
char distStr[10] = "";
|
||||
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
if (nodeDB->hasValidPosition(ourNode) && nodeDB->hasValidPosition(node)) {
|
||||
double lat1 = ourNode->position.latitude_i * 1e-7;
|
||||
double lon1 = ourNode->position.longitude_i * 1e-7;
|
||||
double lat2 = node->position.latitude_i * 1e-7;
|
||||
double lon2 = node->position.longitude_i * 1e-7;
|
||||
double lat1 = ourNode->latitude_i * 1e-7;
|
||||
double lon1 = ourNode->longitude_i * 1e-7;
|
||||
double lat2 = node->latitude_i * 1e-7;
|
||||
double lon2 = node->longitude_i * 1e-7;
|
||||
|
||||
double earthRadiusKm = 6371.0;
|
||||
double dLat = (lat2 - lat1) * DEG_TO_RAD;
|
||||
@@ -312,7 +318,7 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawStringMaxWidth(x + ((isHighResolution) ? 6 : 3), y, nameMaxWidth, nodeName);
|
||||
if (node->is_favorite) {
|
||||
if (node && detailIsFavorite(*node)) {
|
||||
if (isHighResolution) {
|
||||
drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
|
||||
} else {
|
||||
@@ -329,7 +335,7 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
}
|
||||
}
|
||||
|
||||
void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
|
||||
void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth)
|
||||
{
|
||||
switch (currentMode) {
|
||||
case MODE_LAST_HEARD:
|
||||
@@ -346,7 +352,7 @@ void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
}
|
||||
}
|
||||
|
||||
void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
|
||||
void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth)
|
||||
{
|
||||
bool isLeftCol = (x < SCREEN_WIDTH / 2);
|
||||
|
||||
@@ -358,7 +364,7 @@ void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawStringMaxWidth(x + ((isHighResolution) ? 6 : 3), y, nameMaxWidth, nodeName);
|
||||
if (node->is_favorite) {
|
||||
if (node && detailIsFavorite(*node)) {
|
||||
if (isHighResolution) {
|
||||
drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
|
||||
} else {
|
||||
@@ -367,7 +373,7 @@ void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
}
|
||||
}
|
||||
|
||||
void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading,
|
||||
void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth, float myHeading,
|
||||
double userLat, double userLon)
|
||||
{
|
||||
if (!nodeDB->hasValidPosition(node))
|
||||
@@ -379,8 +385,8 @@ void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
||||
int centerX = x + columnWidth - arrowXOffset;
|
||||
int centerY = y + FONT_HEIGHT_SMALL / 2;
|
||||
|
||||
double nodeLat = node->position.latitude_i * 1e-7;
|
||||
double nodeLon = node->position.longitude_i * 1e-7;
|
||||
double nodeLat = node->latitude_i * 1e-7;
|
||||
double nodeLon = node->longitude_i * 1e-7;
|
||||
float bearing = GeoCoord::bearing(userLat, userLon, nodeLat, nodeLon);
|
||||
float bearingToNode = RAD_TO_DEG * bearing;
|
||||
float relativeBearing = fmod((bearingToNode - myHeading + 360), 360);
|
||||
@@ -466,16 +472,17 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
||||
int rowCount = 0;
|
||||
|
||||
for (int i = startIndex; i < endIndex; ++i) {
|
||||
if (locationScreen && !nodeDB->getMeshNodeByIndex(i)->has_position) {
|
||||
meshtastic_NodeDetail *candidate = nodeDB->getMeshNodeByIndex(i);
|
||||
if (locationScreen && candidate && !detailHasFlag(*candidate, NODEDETAIL_FLAG_HAS_POSITION)) {
|
||||
numskipped++;
|
||||
continue;
|
||||
}
|
||||
int xPos = x + (col * columnWidth);
|
||||
int yPos = y + yOffset;
|
||||
renderer(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth);
|
||||
renderer(display, candidate, xPos, yPos, columnWidth);
|
||||
|
||||
if (extras) {
|
||||
extras(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth, heading, lat, lon);
|
||||
extras(display, candidate, xPos, yPos, columnWidth, heading, lat, lon);
|
||||
}
|
||||
|
||||
lastNodeY = std::max(lastNodeY, yPos + FONT_HEIGHT_SMALL);
|
||||
@@ -578,9 +585,12 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
{
|
||||
float heading = 0;
|
||||
bool validHeading = false;
|
||||
auto ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
double lat = DegD(ourNode->position.latitude_i);
|
||||
double lon = DegD(ourNode->position.longitude_i);
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
if (!ourNode || !nodeDB->hasValidPosition(ourNode)) {
|
||||
return;
|
||||
}
|
||||
double lat = DegD(ourNode->latitude_i);
|
||||
double lon = DegD(ourNode->longitude_i);
|
||||
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
display->clear();
|
||||
|
||||
@@ -20,8 +20,8 @@ class Screen;
|
||||
namespace NodeListRenderer
|
||||
{
|
||||
// Entry renderer function types
|
||||
typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int);
|
||||
typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int, float, double, double);
|
||||
typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeDetail *, int16_t, int16_t, int);
|
||||
typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeDetail *, int16_t, int16_t, int, float, double, double);
|
||||
|
||||
// Node list mode enumeration
|
||||
enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_COUNT = 3 };
|
||||
@@ -32,14 +32,14 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
||||
double lon = 0);
|
||||
|
||||
// Entry renderers
|
||||
void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
|
||||
void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
|
||||
void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
|
||||
void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
|
||||
void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
|
||||
void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth);
|
||||
void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth);
|
||||
void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth);
|
||||
void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth);
|
||||
void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth);
|
||||
|
||||
// Extras renderers
|
||||
void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading,
|
||||
void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeDetail *node, int16_t x, int16_t y, int columnWidth, float myHeading,
|
||||
double userLat, double userLon);
|
||||
|
||||
// Screen frame functions
|
||||
@@ -51,7 +51,7 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
|
||||
// Utility functions
|
||||
const char *getCurrentModeTitle(int screenWidth);
|
||||
const char *getSafeNodeName(meshtastic_NodeInfoLite *node);
|
||||
const char *getSafeNodeName(OLEDDisplay *display, const meshtastic_NodeDetail *node);
|
||||
void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields);
|
||||
|
||||
// Bitmap drawing function
|
||||
|
||||
@@ -300,14 +300,17 @@ void NotificationRenderer::drawNodePicker(OLEDDisplay *display, OLEDDisplayUiSta
|
||||
int scratchLineNum = 0;
|
||||
for (int i = firstOptionToShow; i < alertBannerOptions && linesShown < visibleTotalLines; i++, linesShown++) {
|
||||
char temp_name[16] = {0};
|
||||
if (nodeDB->getMeshNodeByIndex(i + 1)->has_user) {
|
||||
std::string sanitized = sanitizeString(nodeDB->getMeshNodeByIndex(i + 1)->user.long_name);
|
||||
meshtastic_NodeDetail *candidate = nodeDB->getMeshNodeByIndex(i + 1);
|
||||
if (candidate && detailHasFlag(*candidate, NODEDETAIL_FLAG_HAS_USER) && candidate->long_name[0] != '\0') {
|
||||
std::string sanitized = sanitizeString(candidate->long_name);
|
||||
strncpy(temp_name, sanitized.c_str(), sizeof(temp_name) - 1);
|
||||
} else if (candidate) {
|
||||
snprintf(temp_name, sizeof(temp_name), "(%04X)", static_cast<uint16_t>(candidate->num & 0xFFFF));
|
||||
} else {
|
||||
snprintf(temp_name, sizeof(temp_name), "(%04X)", (uint16_t)(nodeDB->getMeshNodeByIndex(i + 1)->num & 0xFFFF));
|
||||
strncpy(temp_name, "(?)", sizeof(temp_name) - 1);
|
||||
}
|
||||
if (i == curSelected) {
|
||||
selectedNodenum = nodeDB->getMeshNodeByIndex(i + 1)->num;
|
||||
if (candidate && i == curSelected) {
|
||||
selectedNodenum = candidate->num;
|
||||
if (isHighResolution) {
|
||||
strncpy(scratchLineBuffer[scratchLineNum], "> ", 3);
|
||||
strncpy(scratchLineBuffer[scratchLineNum] + 2, temp_name, 36);
|
||||
|
||||
@@ -27,22 +27,22 @@ static uint32_t lastSwitchTime = 0;
|
||||
namespace graphics
|
||||
{
|
||||
NodeNum UIRenderer::currentFavoriteNodeNum = 0;
|
||||
std::vector<meshtastic_NodeInfoLite *> graphics::UIRenderer::favoritedNodes;
|
||||
std::vector<meshtastic_NodeDetail *> graphics::UIRenderer::favoritedNodes;
|
||||
|
||||
void graphics::UIRenderer::rebuildFavoritedNodes()
|
||||
{
|
||||
favoritedNodes.clear();
|
||||
size_t total = nodeDB->getNumMeshNodes();
|
||||
for (size_t i = 0; i < total; i++) {
|
||||
meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i);
|
||||
meshtastic_NodeDetail *n = nodeDB->getMeshNodeByIndex(i);
|
||||
if (!n || n->num == nodeDB->getNodeNum())
|
||||
continue;
|
||||
if (n->is_favorite)
|
||||
if (detailIsFavorite(*n))
|
||||
favoritedNodes.push_back(n);
|
||||
}
|
||||
|
||||
std::sort(favoritedNodes.begin(), favoritedNodes.end(),
|
||||
[](const meshtastic_NodeInfoLite *a, const meshtastic_NodeInfoLite *b) { return a->num < b->num; });
|
||||
[](const meshtastic_NodeDetail *a, const meshtastic_NodeDetail *b) { return a->num < b->num; });
|
||||
}
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_GPS
|
||||
@@ -289,8 +289,8 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
||||
if (nodeIndex < 0 || nodeIndex >= (int)favoritedNodes.size())
|
||||
return;
|
||||
|
||||
meshtastic_NodeInfoLite *node = favoritedNodes[nodeIndex];
|
||||
if (!node || node->num == nodeDB->getNodeNum() || !node->is_favorite)
|
||||
meshtastic_NodeDetail *node = favoritedNodes[nodeIndex];
|
||||
if (!node || node->num == nodeDB->getNodeNum() || !detailIsFavorite(*node))
|
||||
return;
|
||||
display->clear();
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
@@ -303,7 +303,9 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
||||
#endif
|
||||
currentFavoriteNodeNum = node->num;
|
||||
// === Create the shortName and title string ===
|
||||
const char *shortName = (node->has_user && haveGlyphs(node->user.short_name)) ? node->user.short_name : "Node";
|
||||
const bool hasUser = detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER);
|
||||
const char *shortNameCandidate = (hasUser && node->short_name[0] != '\0') ? node->short_name : nullptr;
|
||||
const char *shortName = (shortNameCandidate && haveGlyphs(shortNameCandidate)) ? shortNameCandidate : "Node";
|
||||
char titlestr[32] = {0};
|
||||
snprintf(titlestr, sizeof(titlestr), "Fav: %s", shortName);
|
||||
|
||||
@@ -321,9 +323,9 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
||||
std::string usernameStr;
|
||||
// === 1. Long Name (always try to show first) ===
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
const char *username = (node->has_user && node->user.long_name[0]) ? node->user.short_name : nullptr;
|
||||
const char *username = (hasUser && node->long_name[0]) ? node->short_name : nullptr;
|
||||
#else
|
||||
const char *username = (node->has_user && node->user.long_name[0]) ? node->user.long_name : nullptr;
|
||||
const char *username = (hasUser && node->long_name[0]) ? node->long_name : nullptr;
|
||||
#endif
|
||||
|
||||
if (username) {
|
||||
@@ -349,7 +351,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
||||
haveSignal = true;
|
||||
}
|
||||
// If hops is valid (>0), show right after signal
|
||||
if (node->hops_away > 0) {
|
||||
if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY) && node->hops_away > 0) {
|
||||
size_t len = strlen(signalHopsStr);
|
||||
// Decide between "1 Hop" and "N Hops"
|
||||
if (haveSignal) {
|
||||
@@ -383,23 +385,23 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
||||
#if !defined(M5STACK_UNITC6L)
|
||||
// === 4. Uptime (only show if metric is present) ===
|
||||
char uptimeStr[32] = "";
|
||||
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
|
||||
getUptimeStr(node->device_metrics.uptime_seconds * 1000, " Up", uptimeStr, sizeof(uptimeStr));
|
||||
if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_UPTIME)) {
|
||||
getUptimeStr(node->uptime_seconds * 1000, " Up", uptimeStr, sizeof(uptimeStr));
|
||||
}
|
||||
if (uptimeStr[0] && line < 5) {
|
||||
display->drawString(x, getTextPositions(display)[line++], uptimeStr);
|
||||
}
|
||||
|
||||
// === 5. Distance (only if both nodes have GPS position) ===
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
char distStr[24] = ""; // Make buffer big enough for any string
|
||||
bool haveDistance = false;
|
||||
|
||||
if (nodeDB->hasValidPosition(ourNode) && nodeDB->hasValidPosition(node)) {
|
||||
double lat1 = ourNode->position.latitude_i * 1e-7;
|
||||
double lon1 = ourNode->position.longitude_i * 1e-7;
|
||||
double lat2 = node->position.latitude_i * 1e-7;
|
||||
double lon2 = node->position.longitude_i * 1e-7;
|
||||
double lat1 = ourNode->latitude_i * 1e-7;
|
||||
double lon1 = ourNode->longitude_i * 1e-7;
|
||||
double lat2 = node->latitude_i * 1e-7;
|
||||
double lon2 = node->longitude_i * 1e-7;
|
||||
double earthRadiusKm = 6371.0;
|
||||
double dLat = (lat2 - lat1) * DEG_TO_RAD;
|
||||
double dLon = (lon2 - lon1) * DEG_TO_RAD;
|
||||
@@ -457,6 +459,10 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
||||
showCompass = true;
|
||||
}
|
||||
if (showCompass) {
|
||||
const double ourLatDeg = DegD(ourNode->latitude_i);
|
||||
const double ourLonDeg = DegD(ourNode->longitude_i);
|
||||
const double nodeLatDeg = DegD(node->latitude_i);
|
||||
const double nodeLonDeg = DegD(node->longitude_i);
|
||||
const int16_t topY = getTextPositions(display)[1];
|
||||
const int16_t bottomY = SCREEN_HEIGHT - (FONT_HEIGHT_SMALL - 1);
|
||||
const int16_t usableHeight = bottomY - topY - 5;
|
||||
@@ -467,16 +473,10 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
||||
const int16_t compassX = x + SCREEN_WIDTH - compassRadius - 8;
|
||||
const int16_t compassY = topY + (usableHeight / 2) + ((FONT_HEIGHT_SMALL - 1) / 2) + 2;
|
||||
|
||||
const auto &op = ourNode->position;
|
||||
float myHeading = screen->hasHeading() ? screen->getHeading() * PI / 180
|
||||
: screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
float myHeading =
|
||||
screen->hasHeading() ? screen->getHeading() * PI / 180 : screen->estimatedHeading(ourLatDeg, ourLonDeg);
|
||||
|
||||
const auto &p = node->position;
|
||||
/* unused
|
||||
float d =
|
||||
GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
*/
|
||||
float bearing = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i));
|
||||
float bearing = GeoCoord::bearing(ourLatDeg, ourLonDeg, nodeLatDeg, nodeLonDeg);
|
||||
if (uiconfig.compass_mode == meshtastic_CompassMode_FREEZE_HEADING) {
|
||||
myHeading = 0;
|
||||
} else {
|
||||
@@ -520,20 +520,21 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
|
||||
int compassX = x + SCREEN_WIDTH / 2;
|
||||
int compassY = yBelowContent + availableHeight / 2;
|
||||
|
||||
const auto &op = ourNode->position;
|
||||
const double ourLatDeg = DegD(ourNode->latitude_i);
|
||||
const double ourLonDeg = DegD(ourNode->longitude_i);
|
||||
float myHeading = 0;
|
||||
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
|
||||
myHeading = screen->hasHeading() ? screen->getHeading() * PI / 180
|
||||
: screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
myHeading =
|
||||
screen->hasHeading() ? screen->getHeading() * PI / 180 : screen->estimatedHeading(ourLatDeg, ourLonDeg);
|
||||
}
|
||||
graphics::CompassRenderer::drawCompassNorth(display, compassX, compassY, myHeading, compassRadius);
|
||||
|
||||
const auto &p = node->position;
|
||||
const double nodeLatDeg = DegD(node->latitude_i);
|
||||
const double nodeLonDeg = DegD(node->longitude_i);
|
||||
/* unused
|
||||
float d =
|
||||
GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
float d = GeoCoord::latLongToMeter(nodeLatDeg, nodeLonDeg, ourLatDeg, ourLonDeg);
|
||||
*/
|
||||
float bearing = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i));
|
||||
float bearing = GeoCoord::bearing(ourLatDeg, ourLonDeg, nodeLatDeg, nodeLonDeg);
|
||||
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING)
|
||||
bearing -= myHeading;
|
||||
graphics::CompassRenderer::drawNodeHeading(display, compassX, compassY, compassRadius * 2, bearing);
|
||||
@@ -555,7 +556,7 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
int line = 1;
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
|
||||
// === Header ===
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
@@ -723,8 +724,8 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
|
||||
int yOffset = (isHighResolution) ? 0 : 5;
|
||||
std::string longNameStr;
|
||||
|
||||
if (ourNode && ourNode->has_user && strlen(ourNode->user.long_name) > 0) {
|
||||
longNameStr = sanitizeString(ourNode->user.long_name);
|
||||
if (ourNode && detailHasFlag(*ourNode, NODEDETAIL_FLAG_HAS_USER) && ourNode->long_name[0] != '\0') {
|
||||
longNameStr = sanitizeString(ourNode->long_name);
|
||||
}
|
||||
char shortnameble[35];
|
||||
snprintf(shortnameble, sizeof(shortnameble), "%s",
|
||||
@@ -981,8 +982,6 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
|
||||
config.display.heading_bold = false;
|
||||
|
||||
const char *displayLine = ""; // Initialize to empty string by default
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
|
||||
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||
if (config.position.fixed_position) {
|
||||
displayLine = "Fixed GPS";
|
||||
|
||||
@@ -63,7 +63,7 @@ class UIRenderer
|
||||
static void drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||
|
||||
static NodeNum currentFavoriteNodeNum;
|
||||
static std::vector<meshtastic_NodeInfoLite *> favoritedNodes;
|
||||
static std::vector<meshtastic_NodeDetail *> favoritedNodes;
|
||||
static void rebuildFavoritedNodes();
|
||||
|
||||
// OEM screens
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "main.h"
|
||||
|
||||
#include "RTC.h"
|
||||
#include "mesh/NodeDB.h"
|
||||
|
||||
using namespace NicheGraphics;
|
||||
|
||||
@@ -333,13 +334,13 @@ std::string InkHUD::Applet::parse(std::string text)
|
||||
// Get the best version of a node's short name available to us
|
||||
// Parses any non-ascii chars
|
||||
// Swaps for last-four of node-id if the real short name is unknown or can't be rendered (emoji)
|
||||
std::string InkHUD::Applet::parseShortName(meshtastic_NodeInfoLite *node)
|
||||
std::string InkHUD::Applet::parseShortName(meshtastic_NodeDetail *node)
|
||||
{
|
||||
assert(node);
|
||||
|
||||
// Use the true shortname if known, and doesn't contain any unprintable characters (emoji, etc.)
|
||||
if (node->has_user) {
|
||||
std::string parsed = parse(node->user.short_name);
|
||||
if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER) && node->short_name[0] != '\0') {
|
||||
std::string parsed = parse(node->short_name);
|
||||
if (isPrintable(parsed))
|
||||
return parsed;
|
||||
}
|
||||
@@ -640,7 +641,9 @@ uint16_t InkHUD::Applet::getActiveNodeCount()
|
||||
|
||||
// For each node in db
|
||||
for (uint16_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||
const meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i);
|
||||
if (!node)
|
||||
continue;
|
||||
|
||||
// Check if heard recently, and not our own node
|
||||
if (sinceLastSeen(node) < settings->recentlyActiveSeconds && node->num != nodeDB->getNodeNum())
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <GFX.h> // GFXRoot drawing lib
|
||||
|
||||
#include "mesh/MeshTypes.h"
|
||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||
|
||||
#include "./AppletFont.h"
|
||||
#include "./Applets/System/Notification/Notification.h" // The notification object, not the applet
|
||||
@@ -133,15 +134,15 @@ class Applet : public GFX
|
||||
void drawLogo(int16_t centerX, int16_t centerY, uint16_t width, uint16_t height,
|
||||
Color color = BLACK); // Draw the Meshtastic logo
|
||||
|
||||
std::string hexifyNodeNum(NodeNum num); // Style as !0123abdc
|
||||
SignalStrength getSignalStrength(float snr, float rssi); // Interpret SNR and RSSI, as an easy to understand value
|
||||
std::string getTimeString(uint32_t epochSeconds); // Human readable
|
||||
std::string getTimeString(); // Current time, human readable
|
||||
uint16_t getActiveNodeCount(); // Duration determined by user, in onscreen menu
|
||||
std::string localizeDistance(uint32_t meters); // Human readable distance, imperial or metric
|
||||
std::string parse(std::string text); // Handle text which might contain special chars
|
||||
std::string parseShortName(meshtastic_NodeInfoLite *node); // Get the shortname, or a substitute if has unprintable chars
|
||||
bool isPrintable(std::string); // Check for characters which the font can't print
|
||||
std::string hexifyNodeNum(NodeNum num); // Style as !0123abdc
|
||||
SignalStrength getSignalStrength(float snr, float rssi); // Interpret SNR and RSSI, as an easy to understand value
|
||||
std::string getTimeString(uint32_t epochSeconds); // Human readable
|
||||
std::string getTimeString(); // Current time, human readable
|
||||
uint16_t getActiveNodeCount(); // Duration determined by user, in onscreen menu
|
||||
std::string localizeDistance(uint32_t meters); // Human readable distance, imperial or metric
|
||||
std::string parse(std::string text); // Handle text which might contain special chars
|
||||
std::string parseShortName(meshtastic_NodeDetail *node); // Get the shortname, or a substitute if has unprintable chars
|
||||
bool isPrintable(std::string); // Check for characters which the font can't print
|
||||
|
||||
// Convenient references
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "./MapApplet.h"
|
||||
|
||||
#include "mesh/NodeDB.h"
|
||||
|
||||
using namespace NicheGraphics;
|
||||
|
||||
void InkHUD::MapApplet::onRender()
|
||||
@@ -136,9 +138,9 @@ void InkHUD::MapApplet::onRender()
|
||||
printAt(vertBarX + (bottomLabelW / 2) + 1, bottomLabelY + (bottomLabelH / 2), vertBottomLabel, CENTER, MIDDLE);
|
||||
|
||||
// Draw our node LAST with full white fill + outline
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
if (ourNode && nodeDB->hasValidPosition(ourNode)) {
|
||||
Marker self = calculateMarker(ourNode->position.latitude_i * 1e-7, ourNode->position.longitude_i * 1e-7, false, 0);
|
||||
Marker self = calculateMarker(ourNode->latitude_i * 1e-7f, ourNode->longitude_i * 1e-7f, false, 0);
|
||||
|
||||
int16_t centerX = X(0.5) + (self.eastMeters * metersToPx);
|
||||
int16_t centerY = Y(0.5) - (self.northMeters * metersToPx);
|
||||
@@ -166,10 +168,10 @@ void InkHUD::MapApplet::onRender()
|
||||
void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
|
||||
{
|
||||
// If we have a valid position for our own node, use that as the anchor
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
if (ourNode && nodeDB->hasValidPosition(ourNode)) {
|
||||
*lat = ourNode->position.latitude_i * 1e-7;
|
||||
*lng = ourNode->position.longitude_i * 1e-7;
|
||||
*lat = ourNode->latitude_i * 1e-7f;
|
||||
*lng = ourNode->longitude_i * 1e-7f;
|
||||
} else {
|
||||
// Find mean lat long coords
|
||||
// ============================
|
||||
@@ -189,7 +191,7 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
|
||||
|
||||
// For each node in db
|
||||
for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i);
|
||||
|
||||
// Skip if no position
|
||||
if (!nodeDB->hasValidPosition(node))
|
||||
@@ -200,8 +202,8 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
|
||||
continue;
|
||||
|
||||
// Latitude and Longitude of node, in radians
|
||||
float latRad = node->position.latitude_i * (1e-7) * DEG_TO_RAD;
|
||||
float lngRad = node->position.longitude_i * (1e-7) * DEG_TO_RAD;
|
||||
float latRad = node->latitude_i * (1e-7f) * DEG_TO_RAD;
|
||||
float lngRad = node->longitude_i * (1e-7f) * DEG_TO_RAD;
|
||||
|
||||
// Convert to cartesian points, with center of earth at 0, 0, 0
|
||||
// Exact distance from center is irrelevant, as we're only interested in the vector
|
||||
@@ -288,7 +290,7 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
|
||||
float westernmost = lngCenter;
|
||||
|
||||
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i);
|
||||
|
||||
// Skip if no position
|
||||
if (!nodeDB->hasValidPosition(node))
|
||||
@@ -299,12 +301,12 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
|
||||
continue;
|
||||
|
||||
// Check for a new top or bottom latitude
|
||||
float latNode = node->position.latitude_i * 1e-7;
|
||||
float latNode = node->latitude_i * 1e-7f;
|
||||
northernmost = max(northernmost, latNode);
|
||||
southernmost = min(southernmost, latNode);
|
||||
|
||||
// Longitude is trickier
|
||||
float lngNode = node->position.longitude_i * 1e-7;
|
||||
float lngNode = node->longitude_i * 1e-7f;
|
||||
float degEastward = fmod(((lngNode - lngCenter) + 360), 360); // Degrees traveled east from lngCenter to reach node
|
||||
float degWestward = abs(fmod(((lngNode - lngCenter) - 360), 360)); // Degrees traveled west from lngCenter to reach node
|
||||
if (degEastward < degWestward)
|
||||
@@ -366,14 +368,14 @@ InkHUD::MapApplet::Marker InkHUD::MapApplet::calculateMarker(float lat, float ln
|
||||
return m;
|
||||
}
|
||||
// Draw a marker on the map for a node, with a shortname label, and backing box
|
||||
void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
|
||||
void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeDetail *node)
|
||||
{
|
||||
// Find x and y position based on node's position in nodeDB
|
||||
assert(nodeDB->hasValidPosition(node));
|
||||
Marker m = calculateMarker(node->position.latitude_i * 1e-7, // Lat, converted from Meshtastic's internal int32 style
|
||||
node->position.longitude_i * 1e-7, // Long, converted from Meshtastic's internal int32 style
|
||||
node->has_hops_away, // Is the hopsAway number valid
|
||||
node->hops_away // Hops away
|
||||
Marker m = calculateMarker(node->latitude_i * 1e-7f, // Lat, converted from Meshtastic's internal int32 style
|
||||
node->longitude_i * 1e-7f, // Long, converted from Meshtastic's internal int32 style
|
||||
detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY), // Is the hopsAway number valid
|
||||
node->hops_away // Hops away
|
||||
);
|
||||
|
||||
// Convert to pixel coords
|
||||
@@ -396,9 +398,9 @@ void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
|
||||
uint16_t labelH;
|
||||
uint8_t markerSize;
|
||||
|
||||
bool tooManyHops = node->hops_away > config.lora.hop_limit;
|
||||
bool tooManyHops = detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY) && node->hops_away > config.lora.hop_limit;
|
||||
bool isOurNode = node->num == nodeDB->getNodeNum();
|
||||
bool unknownHops = !node->has_hops_away && !isOurNode;
|
||||
bool unknownHops = !detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY) && !isOurNode;
|
||||
|
||||
// Parse any non-ascii chars in the short name,
|
||||
// and use last 4 instead if unknown / can't render
|
||||
@@ -476,7 +478,7 @@ bool InkHUD::MapApplet::enoughMarkers()
|
||||
{
|
||||
size_t count = 0;
|
||||
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i);
|
||||
|
||||
// Count nodes
|
||||
if (nodeDB->hasValidPosition(node) && shouldDrawNode(node))
|
||||
@@ -499,7 +501,7 @@ void InkHUD::MapApplet::calculateAllMarkers()
|
||||
|
||||
// For each node in db
|
||||
for (uint32_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i);
|
||||
|
||||
// Skip if no position
|
||||
if (!nodeDB->hasValidPosition(node))
|
||||
@@ -515,12 +517,11 @@ void InkHUD::MapApplet::calculateAllMarkers()
|
||||
continue;
|
||||
|
||||
// Calculate marker and store it
|
||||
markers.push_back(
|
||||
calculateMarker(node->position.latitude_i * 1e-7, // Lat, converted from Meshtastic's internal int32 style
|
||||
node->position.longitude_i * 1e-7, // Long, converted from Meshtastic's internal int32 style
|
||||
node->has_hops_away, // Is the hopsAway number valid
|
||||
node->hops_away // Hops away
|
||||
));
|
||||
markers.push_back(calculateMarker(node->latitude_i * 1e-7f, // Lat, converted from Meshtastic's internal int32 style
|
||||
node->longitude_i * 1e-7f, // Long, converted from Meshtastic's internal int32 style
|
||||
detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY), // Is the hopsAway number valid
|
||||
node->hops_away // Hops away
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,12 @@ class MapApplet : public Applet
|
||||
void onRender() override;
|
||||
|
||||
protected:
|
||||
virtual bool shouldDrawNode(meshtastic_NodeInfoLite *node) { return true; } // Allow derived applets to filter the nodes
|
||||
virtual bool shouldDrawNode(meshtastic_NodeDetail *node) { return true; } // Allow derived applets to filter the nodes
|
||||
virtual void getMapCenter(float *lat, float *lng);
|
||||
virtual void getMapSize(uint32_t *widthMeters, uint32_t *heightMeters);
|
||||
|
||||
bool enoughMarkers(); // Anything to draw?
|
||||
void drawLabeledMarker(meshtastic_NodeInfoLite *node); // Highlight a specific marker
|
||||
bool enoughMarkers(); // Anything to draw?
|
||||
void drawLabeledMarker(meshtastic_NodeDetail *node); // Highlight a specific marker
|
||||
|
||||
private:
|
||||
// Position and size of a marker to be drawn
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include "RTC.h"
|
||||
|
||||
#include "GeoCoord.h"
|
||||
#include "NodeDB.h"
|
||||
#include "mesh/NodeDB.h"
|
||||
|
||||
#include "./NodeListApplet.h"
|
||||
|
||||
@@ -50,19 +50,19 @@ ProcessMessage InkHUD::NodeListApplet::handleReceived(const meshtastic_MeshPacke
|
||||
c.signal = getSignalStrength(mp.rx_snr, mp.rx_rssi);
|
||||
|
||||
// Assemble info: from nodeDB (needed to detect changes)
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(c.nodeNum);
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(c.nodeNum);
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
if (node) {
|
||||
if (node->has_hops_away)
|
||||
if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY))
|
||||
c.hopsAway = node->hops_away;
|
||||
|
||||
if (nodeDB->hasValidPosition(node) && nodeDB->hasValidPosition(ourNode)) {
|
||||
// Get lat and long as float
|
||||
// Meshtastic stores these as integers internally
|
||||
float ourLat = ourNode->position.latitude_i * 1e-7;
|
||||
float ourLong = ourNode->position.longitude_i * 1e-7;
|
||||
float theirLat = node->position.latitude_i * 1e-7;
|
||||
float theirLong = node->position.longitude_i * 1e-7;
|
||||
float ourLat = ourNode->latitude_i * 1e-7f;
|
||||
float ourLong = ourNode->longitude_i * 1e-7f;
|
||||
float theirLat = node->latitude_i * 1e-7f;
|
||||
float theirLong = node->longitude_i * 1e-7f;
|
||||
|
||||
c.distanceMeters = (int32_t)GeoCoord::latLongToMeter(theirLat, theirLong, ourLat, ourLong);
|
||||
}
|
||||
@@ -144,7 +144,7 @@ void InkHUD::NodeListApplet::onRender()
|
||||
std::string distance; // handled below;
|
||||
uint8_t &hopsAway = card->hopsAway;
|
||||
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeNum);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeNum);
|
||||
|
||||
// Skip deleted nodes
|
||||
if (!node) {
|
||||
@@ -162,8 +162,8 @@ void InkHUD::NodeListApplet::onRender()
|
||||
// -- Longname --
|
||||
// Parse special chars in long name
|
||||
// Use node id if unknown
|
||||
if (node && node->has_user)
|
||||
longName = parse(node->user.long_name); // Found in nodeDB
|
||||
if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER))
|
||||
longName = parse(node->long_name); // Found in nodeDB
|
||||
else {
|
||||
// Not found in nodeDB, show a hex nodeid instead
|
||||
longName = hexifyNodeNum(nodeNum);
|
||||
|
||||
@@ -14,10 +14,10 @@ InkHUD::LogoApplet::LogoApplet() : concurrency::OSThread("LogoApplet")
|
||||
// During onboarding, show the default short name as well as the version string
|
||||
// This behavior assists manufacturers during mass production, and should not be modified without good reason
|
||||
if (!settings->tips.safeShutdownSeen) {
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
fontTitle = fontMedium;
|
||||
textLeft = xstr(APP_VERSION_SHORT);
|
||||
textRight = parseShortName(ourNode);
|
||||
textRight = ourNode ? parseShortName(ourNode) : "";
|
||||
textTitle = "Meshtastic";
|
||||
} else {
|
||||
fontTitle = fontSmall;
|
||||
@@ -146,10 +146,10 @@ void InkHUD::LogoApplet::onShutdown()
|
||||
|
||||
// Prepare for the powered-off screen now
|
||||
// We can change these values because the initial "shutting down" screen has already rendered at this point
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
textLeft = "";
|
||||
textRight = "";
|
||||
textTitle = parseShortName(ourNode);
|
||||
textTitle = ourNode ? parseShortName(ourNode) : "";
|
||||
fontTitle = fontMedium;
|
||||
|
||||
// This is then drawn by InkHUD::Events::onShutdown, with a blocking FULL update, after InkHUD's flash write is complete
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "Router.h"
|
||||
#include "airtime.h"
|
||||
#include "main.h"
|
||||
#include "mesh/NodeDB.h"
|
||||
#include "power.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_GPS
|
||||
@@ -616,7 +617,8 @@ void InkHUD::MenuApplet::populateRecipientPage()
|
||||
|
||||
// Count favorites
|
||||
for (uint32_t i = 0; i < nodeCount; i++) {
|
||||
if (nodeDB->getMeshNodeByIndex(i)->is_favorite)
|
||||
const meshtastic_NodeDetail *detail = nodeDB->getMeshNodeByIndex(i);
|
||||
if (detail && detailIsFavorite(*detail))
|
||||
favoriteCount++;
|
||||
}
|
||||
|
||||
@@ -624,10 +626,12 @@ void InkHUD::MenuApplet::populateRecipientPage()
|
||||
// Don't want some monstrous list that takes 100 clicks to reach exit
|
||||
if (favoriteCount < 20) {
|
||||
for (uint32_t i = 0; i < nodeCount; i++) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||
const meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i);
|
||||
if (!node)
|
||||
continue;
|
||||
|
||||
// Skip node if not a favorite
|
||||
if (!node->is_favorite)
|
||||
if (!detailIsFavorite(*node))
|
||||
continue;
|
||||
|
||||
CannedMessages::RecipientItem r;
|
||||
@@ -637,8 +641,8 @@ void InkHUD::MenuApplet::populateRecipientPage()
|
||||
|
||||
// Set a label for the menu item
|
||||
r.label = "DM: ";
|
||||
if (node->has_user)
|
||||
r.label += parse(node->user.long_name);
|
||||
if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER) && node->long_name[0] != '\0')
|
||||
r.label += parse(node->long_name);
|
||||
else
|
||||
r.label += hexifyNodeNum(node->num); // Unsure if it's possible to favorite a node without NodeInfo?
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "./Notification.h"
|
||||
#include "graphics/niche/InkHUD/Persistence.h"
|
||||
|
||||
#include "mesh/NodeDB.h"
|
||||
#include "meshUtils.h"
|
||||
#include "modules/TextMessageModule.h"
|
||||
|
||||
@@ -206,13 +207,13 @@ std::string InkHUD::NotificationApplet::getNotificationText(uint16_t widthAvaila
|
||||
isBroadcast ? &inkhud->persistence->latestMessage.broadcast : &inkhud->persistence->latestMessage.dm;
|
||||
|
||||
// Find info about the sender
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(message->sender);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(message->sender);
|
||||
|
||||
// Leading tag (channel vs. DM)
|
||||
text += isBroadcast ? "From:" : "DM: ";
|
||||
|
||||
// Sender id
|
||||
if (node && node->has_user)
|
||||
if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER))
|
||||
text += parseShortName(node);
|
||||
else
|
||||
text += hexifyNodeNum(message->sender);
|
||||
@@ -226,7 +227,7 @@ std::string InkHUD::NotificationApplet::getNotificationText(uint16_t widthAvaila
|
||||
text += isBroadcast ? "Msg from " : "DM from ";
|
||||
|
||||
// Sender id
|
||||
if (node && node->has_user)
|
||||
if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER))
|
||||
text += parseShortName(node);
|
||||
else
|
||||
text += hexifyNodeNum(message->sender);
|
||||
|
||||
@@ -69,11 +69,11 @@ void InkHUD::AllMessageApplet::onRender()
|
||||
// Sender's id
|
||||
// - short name and long name, if available, or
|
||||
// - node id
|
||||
meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(message->sender);
|
||||
if (sender && sender->has_user) {
|
||||
meshtastic_NodeDetail *sender = nodeDB->getMeshNode(message->sender);
|
||||
if (sender && detailHasFlag(*sender, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
header += parseShortName(sender); // May be last-four of node if unprintable (emoji, etc)
|
||||
header += " (";
|
||||
header += parse(sender->user.long_name);
|
||||
header += parse(sender->long_name);
|
||||
header += ")";
|
||||
} else
|
||||
header += hexifyNodeNum(message->sender);
|
||||
|
||||
@@ -65,11 +65,11 @@ void InkHUD::DMApplet::onRender()
|
||||
// Sender's id
|
||||
// - shortname and long name, if available, or
|
||||
// - node id
|
||||
meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(latestMessage->dm.sender);
|
||||
if (sender && sender->has_user) {
|
||||
meshtastic_NodeDetail *sender = nodeDB->getMeshNode(latestMessage->dm.sender);
|
||||
if (sender && detailHasFlag(*sender, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
header += parseShortName(sender); // May be last-four of node if unprintable (emoji, etc)
|
||||
header += " (";
|
||||
header += parse(sender->user.long_name);
|
||||
header += parse(sender->long_name);
|
||||
header += ")";
|
||||
} else
|
||||
header += hexifyNodeNum(latestMessage->dm.sender);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#include "./HeardApplet.h"
|
||||
|
||||
#include "mesh/NodeDB.h"
|
||||
|
||||
using namespace NicheGraphics;
|
||||
|
||||
void InkHUD::HeardApplet::onActivate()
|
||||
@@ -61,7 +63,7 @@ void InkHUD::HeardApplet::handleParsed(CardInfo c)
|
||||
void InkHUD::HeardApplet::populateFromNodeDB()
|
||||
{
|
||||
// Fill a collection with pointers to each node in db
|
||||
std::vector<meshtastic_NodeInfoLite *> ordered;
|
||||
std::vector<meshtastic_NodeDetail *> ordered;
|
||||
for (auto mn = nodeDB->meshNodes->begin(); mn != nodeDB->meshNodes->end(); ++mn) {
|
||||
// Only copy if valid, and not our own node
|
||||
if (mn->num != 0 && mn->num != nodeDB->getNodeNum())
|
||||
@@ -69,7 +71,7 @@ void InkHUD::HeardApplet::populateFromNodeDB()
|
||||
}
|
||||
|
||||
// Sort the collection by age
|
||||
std::sort(ordered.begin(), ordered.end(), [](meshtastic_NodeInfoLite *top, meshtastic_NodeInfoLite *bottom) -> bool {
|
||||
std::sort(ordered.begin(), ordered.end(), [](const meshtastic_NodeDetail *top, const meshtastic_NodeDetail *bottom) -> bool {
|
||||
return (top->last_heard > bottom->last_heard);
|
||||
});
|
||||
|
||||
@@ -79,21 +81,21 @@ void InkHUD::HeardApplet::populateFromNodeDB()
|
||||
ordered.resize(maxCards());
|
||||
|
||||
// Create card info for these (stale) node observations
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
for (meshtastic_NodeInfoLite *node : ordered) {
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
for (meshtastic_NodeDetail *node : ordered) {
|
||||
CardInfo c;
|
||||
c.nodeNum = node->num;
|
||||
|
||||
if (node->has_hops_away)
|
||||
if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_HOPS_AWAY))
|
||||
c.hopsAway = node->hops_away;
|
||||
|
||||
if (nodeDB->hasValidPosition(node) && nodeDB->hasValidPosition(ourNode)) {
|
||||
// Get lat and long as float
|
||||
// Meshtastic stores these as integers internally
|
||||
float ourLat = ourNode->position.latitude_i * 1e-7;
|
||||
float ourLong = ourNode->position.longitude_i * 1e-7;
|
||||
float theirLat = node->position.latitude_i * 1e-7;
|
||||
float theirLong = node->position.longitude_i * 1e-7;
|
||||
float ourLat = ourNode->latitude_i * 1e-7f;
|
||||
float ourLong = ourNode->longitude_i * 1e-7f;
|
||||
float theirLat = node->latitude_i * 1e-7f;
|
||||
float theirLong = node->longitude_i * 1e-7f;
|
||||
|
||||
c.distanceMeters = (int32_t)GeoCoord::latLongToMeter(theirLat, theirLong, ourLat, ourLong);
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ void InkHUD::PositionsApplet::onRender()
|
||||
// We might be rendering because we got a position packet from them
|
||||
// We might be rendering because our own position updated
|
||||
// Either way, we still highlight which node most recently sent us a position packet
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(lastFrom);
|
||||
const meshtastic_NodeDetail *node = nodeDB->getMeshNode(lastFrom);
|
||||
if (node && nodeDB->hasValidPosition(node) && enoughMarkers())
|
||||
drawLabeledMarker(node);
|
||||
drawLabeledMarker(const_cast<meshtastic_NodeDetail *>(node));
|
||||
}
|
||||
|
||||
// Determine if we need to redraw the map, when we receive a new position packet
|
||||
|
||||
@@ -108,8 +108,8 @@ void InkHUD::ThreadedMessageApplet::onRender()
|
||||
info += "Me";
|
||||
else {
|
||||
// Check if sender is node db
|
||||
meshtastic_NodeInfoLite *sender = nodeDB->getMeshNode(m.sender);
|
||||
if (sender)
|
||||
meshtastic_NodeDetail *sender = nodeDB->getMeshNode(m.sender);
|
||||
if (sender && detailHasFlag(*sender, NODEDETAIL_FLAG_HAS_USER))
|
||||
info += parseShortName(sender); // Handle any unprintable chars in short name
|
||||
else
|
||||
info += hexifyNodeNum(m.sender); // No node info at all. Print the node num
|
||||
|
||||
@@ -86,12 +86,15 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
|
||||
|
||||
nodeDB->updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
|
||||
bool isPreferredRebroadcaster = config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER;
|
||||
meshtastic_NodeDetail *fromNode = nodeDB->getMeshNode(mp->from);
|
||||
|
||||
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag &&
|
||||
mp->decoded.portnum == meshtastic_PortNum_TELEMETRY_APP && mp->decoded.request_id > 0) {
|
||||
LOG_DEBUG("Received telemetry response. Skip sending our NodeInfo");
|
||||
// ignore our request for its NodeInfo
|
||||
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
|
||||
nodeInfoModule && !isPreferredRebroadcaster && !nodeDB->isFull()) {
|
||||
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && fromNode &&
|
||||
!detailHasFlag(*fromNode, NODEDETAIL_FLAG_HAS_USER) && nodeInfoModule && !isPreferredRebroadcaster &&
|
||||
!nodeDB->isFull()) {
|
||||
if (airTime->isTxAllowedChannelUtil(true)) {
|
||||
// Hops used by the request. If somebody in between running modified firmware modified it, ignore it
|
||||
auto hopStart = mp->hop_start;
|
||||
@@ -269,7 +272,7 @@ void MeshService::sendToMesh(meshtastic_MeshPacket *p, RxSource src, bool ccToPh
|
||||
|
||||
bool MeshService::trySendPosition(NodeNum dest, bool wantReplies)
|
||||
{
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
|
||||
assert(node);
|
||||
|
||||
@@ -376,24 +379,25 @@ void MeshService::sendClientNotification(meshtastic_ClientNotification *n)
|
||||
fromNum++;
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode()
|
||||
meshtastic_NodeDetail *MeshService::refreshLocalMeshNode()
|
||||
{
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
assert(node);
|
||||
|
||||
// We might not have a position yet for our local node, in that case, at least try to send the time
|
||||
if (!node->has_position) {
|
||||
memset(&node->position, 0, sizeof(node->position));
|
||||
node->has_position = true;
|
||||
// Ensure we have a position container so time fields stay fresh even without GPS fixes
|
||||
if (!detailHasFlag(*node, NODEDETAIL_FLAG_HAS_POSITION)) {
|
||||
detailSetFlag(*node, NODEDETAIL_FLAG_HAS_POSITION, true);
|
||||
node->latitude_i = 0;
|
||||
node->longitude_i = 0;
|
||||
node->altitude = 0;
|
||||
node->position_source = _meshtastic_Position_LocSource_MIN;
|
||||
}
|
||||
|
||||
meshtastic_PositionLite &position = node->position;
|
||||
|
||||
// Update our local node info with our time (even if we don't decide to update anyone else)
|
||||
node->last_heard =
|
||||
getValidTime(RTCQualityFromNet); // This nodedb timestamp might be stale, so update it if our clock is kinda valid
|
||||
|
||||
position.time = getValidTime(RTCQualityFromNet);
|
||||
node->position_time = getValidTime(RTCQualityFromNet);
|
||||
|
||||
if (powerStatus->getHasBattery() == 1) {
|
||||
updateBatteryLevel(powerStatus->getBatteryChargePercent());
|
||||
@@ -406,7 +410,7 @@ meshtastic_NodeInfoLite *MeshService::refreshLocalMeshNode()
|
||||
int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
|
||||
{
|
||||
// Update our local node info with our position (even if we don't decide to update anyone else)
|
||||
const meshtastic_NodeInfoLite *node = refreshLocalMeshNode();
|
||||
const meshtastic_NodeDetail *node = refreshLocalMeshNode();
|
||||
meshtastic_Position pos = meshtastic_Position_init_default;
|
||||
|
||||
if (newStatus->getHasLock()) {
|
||||
@@ -421,7 +425,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
|
||||
// Used fixed position if configured regardless of GPS lock
|
||||
if (config.position.fixed_position) {
|
||||
LOG_WARN("Use fixed position");
|
||||
pos = TypeConversions::ConvertToPosition(node->position);
|
||||
pos = TypeConversions::ConvertToPosition(detailToPositionLite(*node));
|
||||
}
|
||||
|
||||
// Add a fresh timestamp
|
||||
|
||||
@@ -170,7 +170,7 @@ class MeshService
|
||||
bool cancelSending(PacketId id);
|
||||
|
||||
/// Pull the latest power and time info into my nodeinfo
|
||||
meshtastic_NodeInfoLite *refreshLocalMeshNode();
|
||||
meshtastic_NodeDetail *refreshLocalMeshNode();
|
||||
|
||||
/// Send a packet to the phone
|
||||
void sendToPhone(meshtastic_MeshPacket *p);
|
||||
|
||||
@@ -94,7 +94,7 @@ void NextHopRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtast
|
||||
// is not 0 (means implicit ACK) and original packet was also relayed by this node, or we sent it directly to the
|
||||
// destination
|
||||
if (p->from != 0) {
|
||||
meshtastic_NodeInfoLite *origTx = nodeDB->getMeshNode(p->from);
|
||||
meshtastic_NodeDetail *origTx = nodeDB->getMeshNode(p->from);
|
||||
if (origTx) {
|
||||
// Either relayer of ACK was also a relayer of the packet, or we were the *only* relayer and the ACK came
|
||||
// directly from the destination
|
||||
@@ -176,7 +176,7 @@ uint8_t NextHopRouter::getNextHop(NodeNum to, uint8_t relay_node)
|
||||
if (isBroadcast(to))
|
||||
return NO_NEXT_HOP_PREFERENCE;
|
||||
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(to);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(to);
|
||||
if (node && node->next_hop) {
|
||||
// We are careful not to return the relay node as the next hop
|
||||
if (node->next_hop != relay_node) {
|
||||
@@ -296,7 +296,7 @@ int32_t NextHopRouter::doRetransmissions()
|
||||
// Last retransmission, reset next_hop (fallback to FloodingRouter)
|
||||
p.packet->next_hop = NO_NEXT_HOP_PREFERENCE;
|
||||
// Also reset it in the nodeDB
|
||||
meshtastic_NodeInfoLite *sentTo = nodeDB->getMeshNode(p.packet->to);
|
||||
meshtastic_NodeDetail *sentTo = nodeDB->getMeshNode(p.packet->to);
|
||||
if (sentTo) {
|
||||
LOG_INFO("Resetting next hop for packet with dest 0x%x\n", p.packet->to);
|
||||
sentTo->next_hop = NO_NEXT_HOP_PREFERENCE;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "modules/NeighborInfoModule.h"
|
||||
#include <ErriezCRC32.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <pb_decode.h>
|
||||
#include <pb_encode.h>
|
||||
#include <vector>
|
||||
@@ -152,18 +153,19 @@ uint32_t get_st7789_id(uint8_t cs, uint8_t sck, uint8_t mosi, uint8_t dc, uint8_
|
||||
bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field)
|
||||
{
|
||||
if (ostream) {
|
||||
std::vector<meshtastic_NodeInfoLite> const *vec = (std::vector<meshtastic_NodeInfoLite> *)field->pData;
|
||||
for (auto item : *vec) {
|
||||
const auto *vec = reinterpret_cast<const std::vector<meshtastic_NodeDetail> *>(field->pData);
|
||||
for (const auto &item : *vec) {
|
||||
if (!pb_encode_tag_for_field(ostream, field))
|
||||
return false;
|
||||
pb_encode_submessage(ostream, meshtastic_NodeInfoLite_fields, &item);
|
||||
if (!pb_encode_submessage(ostream, meshtastic_NodeDetail_fields, &item))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (istream) {
|
||||
meshtastic_NodeInfoLite node; // this gets good data
|
||||
std::vector<meshtastic_NodeInfoLite> *vec = (std::vector<meshtastic_NodeInfoLite> *)field->pData;
|
||||
meshtastic_NodeDetail node = meshtastic_NodeDetail_init_default; // this gets good data
|
||||
auto *vec = reinterpret_cast<std::vector<meshtastic_NodeDetail> *>(field->pData);
|
||||
|
||||
if (istream->bytes_left && pb_decode(istream, meshtastic_NodeInfoLite_fields, &node))
|
||||
if (istream->bytes_left && pb_decode(istream, meshtastic_NodeDetail_fields, &node))
|
||||
vec->push_back(node);
|
||||
}
|
||||
return true;
|
||||
@@ -295,9 +297,8 @@ NodeDB::NodeDB()
|
||||
}
|
||||
#endif
|
||||
// Include our owner in the node db under our nodenum
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getNodeNum());
|
||||
info->user = TypeConversions::ConvertToUserLite(owner);
|
||||
info->has_user = true;
|
||||
meshtastic_NodeDetail *info = getOrCreateMeshNode(getNodeNum());
|
||||
applyUserToDetail(*info, owner);
|
||||
|
||||
// If node database has not been saved for the first time, save it now
|
||||
#ifdef FSCom
|
||||
@@ -520,7 +521,7 @@ void NodeDB::installDefaultNodeDatabase()
|
||||
{
|
||||
LOG_DEBUG("Install default NodeDatabase");
|
||||
nodeDatabase.version = DEVICESTATE_CUR_VER;
|
||||
nodeDatabase.nodes = std::vector<meshtastic_NodeInfoLite>(MAX_NUM_NODES);
|
||||
nodeDatabase.nodes = std::vector<meshtastic_NodeDetail>(MAX_NUM_NODES, makeDefaultDetail());
|
||||
numMeshNodes = 0;
|
||||
meshNodes = &nodeDatabase.nodes;
|
||||
}
|
||||
@@ -992,16 +993,16 @@ void NodeDB::resetNodes(bool keepFavorites)
|
||||
if (keepFavorites) {
|
||||
LOG_INFO("Clearing node database - preserving favorites");
|
||||
for (size_t i = 0; i < meshNodes->size(); i++) {
|
||||
meshtastic_NodeInfoLite &node = meshNodes->at(i);
|
||||
if (i > 0 && !node.is_favorite) {
|
||||
node = meshtastic_NodeInfoLite();
|
||||
meshtastic_NodeDetail &detail = meshNodes->at(i);
|
||||
if (i > 0 && !detailIsFavorite(detail)) {
|
||||
detail = makeDefaultDetail();
|
||||
} else {
|
||||
numMeshNodes += 1;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
LOG_INFO("Clearing node database - removing favorites");
|
||||
std::fill(nodeDatabase.nodes.begin() + 1, nodeDatabase.nodes.end(), meshtastic_NodeInfoLite());
|
||||
std::fill(nodeDatabase.nodes.begin() + 1, nodeDatabase.nodes.end(), makeDefaultDetail());
|
||||
}
|
||||
devicestate.has_rx_text_message = false;
|
||||
devicestate.has_rx_waypoint = false;
|
||||
@@ -1021,19 +1022,17 @@ void NodeDB::removeNodeByNum(NodeNum nodeNum)
|
||||
removed++;
|
||||
}
|
||||
numMeshNodes -= removed;
|
||||
std::fill(nodeDatabase.nodes.begin() + numMeshNodes, nodeDatabase.nodes.begin() + numMeshNodes + 1,
|
||||
meshtastic_NodeInfoLite());
|
||||
std::fill(nodeDatabase.nodes.begin() + numMeshNodes, nodeDatabase.nodes.begin() + numMeshNodes + 1, makeDefaultDetail());
|
||||
LOG_DEBUG("NodeDB::removeNodeByNum purged %d entries. Save changes", removed);
|
||||
saveNodeDatabaseToDisk();
|
||||
}
|
||||
|
||||
void NodeDB::clearLocalPosition()
|
||||
{
|
||||
meshtastic_NodeInfoLite *node = getMeshNode(nodeDB->getNodeNum());
|
||||
node->position.latitude_i = 0;
|
||||
node->position.longitude_i = 0;
|
||||
node->position.altitude = 0;
|
||||
node->position.time = 0;
|
||||
meshtastic_NodeDetail *detail = getMeshNode(nodeDB->getNodeNum());
|
||||
if (detail) {
|
||||
clearPositionFromDetail(*detail);
|
||||
}
|
||||
setLocalPosition(meshtastic_Position_init_default);
|
||||
}
|
||||
|
||||
@@ -1041,10 +1040,10 @@ void NodeDB::cleanupMeshDB()
|
||||
{
|
||||
int newPos = 0, removed = 0;
|
||||
for (int i = 0; i < numMeshNodes; i++) {
|
||||
if (meshNodes->at(i).has_user) {
|
||||
if (meshNodes->at(i).user.public_key.size > 0) {
|
||||
if (memfll(meshNodes->at(i).user.public_key.bytes, 0, meshNodes->at(i).user.public_key.size)) {
|
||||
meshNodes->at(i).user.public_key.size = 0;
|
||||
if (detailHasFlag(meshNodes->at(i), NODEDETAIL_FLAG_HAS_USER)) {
|
||||
if (meshNodes->at(i).public_key.size > 0) {
|
||||
if (memfll(meshNodes->at(i).public_key.bytes, 0, meshNodes->at(i).public_key.size)) {
|
||||
meshNodes->at(i).public_key.size = 0;
|
||||
}
|
||||
}
|
||||
if (newPos != i)
|
||||
@@ -1057,7 +1056,7 @@ void NodeDB::cleanupMeshDB()
|
||||
}
|
||||
numMeshNodes -= removed;
|
||||
std::fill(nodeDatabase.nodes.begin() + numMeshNodes, nodeDatabase.nodes.begin() + numMeshNodes + removed,
|
||||
meshtastic_NodeInfoLite());
|
||||
makeDefaultDetail());
|
||||
LOG_DEBUG("cleanupMeshDB purged %d entries", removed);
|
||||
}
|
||||
|
||||
@@ -1109,14 +1108,14 @@ void NodeDB::pickNewNodeNum()
|
||||
nodeNum = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5];
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *found;
|
||||
while (((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) ||
|
||||
meshtastic_NodeDetail *found;
|
||||
while (((found = getMeshNode(nodeNum)) && memcmp(found->macaddr, ourMacAddr, sizeof(ourMacAddr)) != 0) ||
|
||||
(nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED)) {
|
||||
NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice
|
||||
if (found)
|
||||
LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, by MAC ending in 0x%02x%02x vs our 0x%02x%02x, so "
|
||||
"trying for 0x%x",
|
||||
nodeNum, found->user.macaddr[4], found->user.macaddr[5], ourMacAddr[4], ourMacAddr[5], candidate);
|
||||
nodeNum, found->macaddr[4], found->macaddr[5], ourMacAddr[4], ourMacAddr[5], candidate);
|
||||
nodeNum = candidate;
|
||||
}
|
||||
LOG_DEBUG("Use nodenum 0x%x ", nodeNum);
|
||||
@@ -1415,7 +1414,7 @@ bool NodeDB::saveDeviceStateToDisk()
|
||||
FSCom.mkdir("/prefs");
|
||||
spiLock->unlock();
|
||||
#endif
|
||||
// Note: if MAX_NUM_NODES=100 and meshtastic_NodeInfoLite_size=166, so will be approximately 17KB
|
||||
// Note: if MAX_NUM_NODES=100 and meshtastic_NodeDetail_size=182, node storage alone is roughly 18KB of data
|
||||
// Because so huge we _must_ not use fullAtomic, because the filesystem is probably too small to hold two copies of this
|
||||
return saveProto(deviceStateFileName, meshtastic_DeviceState_size, &meshtastic_DeviceState_msg, &devicestate, true);
|
||||
}
|
||||
@@ -1508,7 +1507,7 @@ bool NodeDB::saveToDisk(int saveWhat)
|
||||
return success;
|
||||
}
|
||||
|
||||
const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
|
||||
const meshtastic_NodeDetail *NodeDB::readNextMeshNode(uint32_t &readIndex)
|
||||
{
|
||||
if (readIndex < numMeshNodes)
|
||||
return &meshNodes->at(readIndex++);
|
||||
@@ -1517,7 +1516,7 @@ const meshtastic_NodeInfoLite *NodeDB::readNextMeshNode(uint32_t &readIndex)
|
||||
}
|
||||
|
||||
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
||||
uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n)
|
||||
uint32_t sinceLastSeen(const meshtastic_NodeDetail *n)
|
||||
{
|
||||
uint32_t now = getTime();
|
||||
|
||||
@@ -1547,9 +1546,10 @@ size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
|
||||
|
||||
// FIXME this implementation is kinda expensive
|
||||
for (int i = 0; i < numMeshNodes; i++) {
|
||||
if (localOnly && meshNodes->at(i).via_mqtt)
|
||||
const auto &detail = meshNodes->at(i);
|
||||
if (localOnly && detailViaMqtt(detail))
|
||||
continue;
|
||||
if (sinceLastSeen(&meshNodes->at(i)) < NUM_ONLINE_SECS)
|
||||
if (sinceLastSeen(&detail) < NUM_ONLINE_SECS)
|
||||
numseen++;
|
||||
}
|
||||
|
||||
@@ -1563,8 +1563,8 @@ size_t NodeDB::getNumOnlineMeshNodes(bool localOnly)
|
||||
*/
|
||||
void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSource src)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
if (!info) {
|
||||
meshtastic_NodeDetail *detail = getOrCreateMeshNode(nodeId);
|
||||
if (!detail) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1574,12 +1574,13 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
p.altitude);
|
||||
|
||||
setLocalPosition(p);
|
||||
info->position = TypeConversions::ConvertToPositionLite(p);
|
||||
applyPositionToDetail(*detail, p);
|
||||
} else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) {
|
||||
// FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO
|
||||
// (stop-gap fix for issue #900)
|
||||
LOG_DEBUG("updatePosition SPECIAL time setting time=%u", p.time);
|
||||
info->position.time = p.time;
|
||||
detailSetFlag(*detail, NODEDETAIL_FLAG_HAS_POSITION, true);
|
||||
detail->position_time = p.time;
|
||||
} else {
|
||||
// Be careful to only update fields that have been set by the REMOTE sender
|
||||
// A lot of position reports don't have time populated. In that case, be careful to not blow away the time we
|
||||
@@ -1589,17 +1590,16 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
LOG_INFO("updatePosition REMOTE node=0x%x time=%u lat=%d lon=%d", nodeId, p.time, p.latitude_i, p.longitude_i);
|
||||
|
||||
// First, back up fields that we want to protect from overwrite
|
||||
uint32_t tmp_time = info->position.time;
|
||||
uint32_t tmp_time = detail->position_time;
|
||||
|
||||
// Next, update atomically
|
||||
info->position = TypeConversions::ConvertToPositionLite(p);
|
||||
applyPositionToDetail(*detail, p);
|
||||
|
||||
// Last, restore any fields that may have been overwritten
|
||||
if (!info->position.time)
|
||||
info->position.time = tmp_time;
|
||||
if (!detail->position_time)
|
||||
detail->position_time = tmp_time;
|
||||
}
|
||||
info->has_position = true;
|
||||
updateGUIforNode = info;
|
||||
updateGUIforNode = detail;
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
}
|
||||
|
||||
@@ -1608,9 +1608,9 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
*/
|
||||
void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxSource src)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
meshtastic_NodeDetail *detail = getOrCreateMeshNode(nodeId);
|
||||
// Environment metrics should never go to NodeDb but we'll safegaurd anyway
|
||||
if (!info || t.which_variant != meshtastic_Telemetry_device_metrics_tag) {
|
||||
if (!detail || t.which_variant != meshtastic_Telemetry_device_metrics_tag) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1620,9 +1620,8 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS
|
||||
} else {
|
||||
LOG_DEBUG("updateTelemetry REMOTE node=0x%x ", nodeId);
|
||||
}
|
||||
info->device_metrics = t.variant.device_metrics;
|
||||
info->has_device_metrics = true;
|
||||
updateGUIforNode = info;
|
||||
applyMetricsToDetail(*detail, t.variant.device_metrics);
|
||||
updateGUIforNode = detail;
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
}
|
||||
|
||||
@@ -1631,32 +1630,32 @@ void NodeDB::updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxS
|
||||
*/
|
||||
void NodeDB::addFromContact(meshtastic_SharedContact contact)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(contact.node_num);
|
||||
if (!info || !contact.has_user) {
|
||||
meshtastic_NodeDetail *detail = getOrCreateMeshNode(contact.node_num);
|
||||
if (!detail || !contact.has_user) {
|
||||
return;
|
||||
}
|
||||
// If the local node has this node marked as manually verified
|
||||
// and the client does not, do not allow the client to update the
|
||||
// saved public key.
|
||||
if ((info->bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) && !contact.manually_verified) {
|
||||
if (contact.user.public_key.size != info->user.public_key.size ||
|
||||
memcmp(contact.user.public_key.bytes, info->user.public_key.bytes, info->user.public_key.size) != 0) {
|
||||
if (detailHasFlag(*detail, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED) && !contact.manually_verified) {
|
||||
if (contact.user.public_key.size != detail->public_key.size ||
|
||||
memcmp(contact.user.public_key.bytes, detail->public_key.bytes, detail->public_key.size) != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
info->num = contact.node_num;
|
||||
info->has_user = true;
|
||||
info->user = TypeConversions::ConvertToUserLite(contact.user);
|
||||
detail->num = contact.node_num;
|
||||
applyUserToDetail(*detail, contact.user);
|
||||
if (contact.should_ignore) {
|
||||
// If should_ignore is set,
|
||||
// we need to clear the public key and other cruft, in addition to setting the node as ignored
|
||||
info->is_ignored = true;
|
||||
info->is_favorite = false;
|
||||
info->has_device_metrics = false;
|
||||
info->has_position = false;
|
||||
info->user.public_key.size = 0;
|
||||
info->user.public_key.bytes[0] = 0;
|
||||
detailSetFlag(*detail, NODEDETAIL_FLAG_IS_IGNORED, true);
|
||||
detailSetFlag(*detail, NODEDETAIL_FLAG_IS_FAVORITE, false);
|
||||
clearMetricsFromDetail(*detail);
|
||||
clearPositionFromDetail(*detail);
|
||||
detail->public_key.size = 0;
|
||||
memset(detail->public_key.bytes, 0, sizeof(detail->public_key.bytes));
|
||||
} else {
|
||||
detailSetFlag(*detail, NODEDETAIL_FLAG_IS_IGNORED, false);
|
||||
/* Clients are sending add_contact before every text message DM (because clients may hold a larger node database with
|
||||
* public keys than the radio holds). However, we don't want to update last_heard just because we sent someone a DM!
|
||||
*/
|
||||
@@ -1670,19 +1669,19 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact)
|
||||
// without the user doing so deliberately. We don't normally expect users to use a CLIENT_BASE to send DMs or to add
|
||||
// contacts, but we should make sure it doesn't auto-favorite in case they do. Instead, as a workaround, we'll set
|
||||
// last_heard to now, so that the add_contact node doesn't immediately get evicted.
|
||||
info->last_heard = getTime();
|
||||
detail->last_heard = getTime();
|
||||
} else {
|
||||
// Normal case: set is_favorite to prevent expiration.
|
||||
// last_heard will remain as-is (or remain 0 if this entry wasn't in the nodeDB).
|
||||
info->is_favorite = true;
|
||||
detailSetFlag(*detail, NODEDETAIL_FLAG_IS_FAVORITE, true);
|
||||
}
|
||||
|
||||
// As the clients will begin sending the contact with DMs, we want to strictly check if the node is manually verified
|
||||
if (contact.manually_verified) {
|
||||
info->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
detailSetFlag(*detail, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED, true);
|
||||
}
|
||||
// Mark the node's key as manually verified to indicate trustworthiness.
|
||||
updateGUIforNode = info;
|
||||
updateGUIforNode = detail;
|
||||
sortMeshDB();
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
}
|
||||
@@ -1693,8 +1692,8 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact)
|
||||
*/
|
||||
bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
if (!info) {
|
||||
meshtastic_NodeDetail *detail = getOrCreateMeshNode(nodeId);
|
||||
if (!detail) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1719,9 +1718,9 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (info->user.public_key.size == 32) { // if we have a key for this user already, don't overwrite with a new one
|
||||
if (detail->public_key.size == 32) { // if we have a key for this user already, don't overwrite with a new one
|
||||
// if the key doesn't match, don't update nodeDB at all.
|
||||
if (p.public_key.size != 32 || (memcmp(p.public_key.bytes, info->user.public_key.bytes, 32) != 0)) {
|
||||
if (p.public_key.size != 32 || (memcmp(p.public_key.bytes, detail->public_key.bytes, 32) != 0)) {
|
||||
LOG_WARN("Public Key mismatch, dropping NodeInfo");
|
||||
return false;
|
||||
}
|
||||
@@ -1734,22 +1733,38 @@ bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelInde
|
||||
// Always ensure user.id is derived from nodeId, regardless of what was received
|
||||
snprintf(p.id, sizeof(p.id), "!%08x", nodeId);
|
||||
|
||||
// Both of info->user and p start as filled with zero so I think this is okay
|
||||
auto lite = TypeConversions::ConvertToUserLite(p);
|
||||
bool changed = memcmp(&info->user, &lite, sizeof(info->user)) || (info->channel != channelIndex);
|
||||
meshtastic_NodeDetail before = *detail;
|
||||
|
||||
info->user = lite;
|
||||
if (info->user.public_key.size == 32) {
|
||||
printBytes("Saved Pubkey: ", info->user.public_key.bytes, 32);
|
||||
applyUserToDetail(*detail, p);
|
||||
|
||||
if (detail->public_key.size == 32) {
|
||||
printBytes("Saved Pubkey: ", detail->public_key.bytes, 32);
|
||||
}
|
||||
if (nodeId != getNodeNum())
|
||||
info->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel)
|
||||
LOG_DEBUG("Update changed=%d user %s/%s, id=0x%08x, channel=%d", changed, info->user.long_name, info->user.short_name, nodeId,
|
||||
info->channel);
|
||||
info->has_user = true;
|
||||
|
||||
if (nodeId != getNodeNum()) {
|
||||
detail->channel = channelIndex; // Set channel we need to use to reach this node (but don't set our own channel)
|
||||
}
|
||||
|
||||
bool userChanged = strncmp(before.long_name, detail->long_name, sizeof(detail->long_name)) != 0 ||
|
||||
strncmp(before.short_name, detail->short_name, sizeof(detail->short_name)) != 0 ||
|
||||
memcmp(before.macaddr, detail->macaddr, sizeof(detail->macaddr)) != 0 ||
|
||||
before.hw_model != detail->hw_model || before.role != detail->role ||
|
||||
before.public_key.size != detail->public_key.size ||
|
||||
memcmp(before.public_key.bytes, detail->public_key.bytes, detail->public_key.size) != 0;
|
||||
|
||||
uint32_t flagDelta = before.flags ^ detail->flags;
|
||||
bool flagChange =
|
||||
(flagDelta & (NODEDETAIL_FLAG_IS_LICENSED | NODEDETAIL_FLAG_HAS_UNMESSAGABLE | NODEDETAIL_FLAG_IS_UNMESSAGABLE)) != 0;
|
||||
|
||||
bool channelChanged = (nodeId != getNodeNum()) && (before.channel != detail->channel);
|
||||
|
||||
bool changed = userChanged || flagChange || channelChanged;
|
||||
|
||||
LOG_DEBUG("Update changed=%d user %s/%s, id=0x%08x, channel=%d", changed, detail->long_name, detail->short_name, nodeId,
|
||||
detail->channel);
|
||||
|
||||
if (changed) {
|
||||
updateGUIforNode = info;
|
||||
updateGUIforNode = detail;
|
||||
notifyObservers(true); // Force an update whether or not our node counts have changed
|
||||
|
||||
// We just changed something about a User,
|
||||
@@ -1777,23 +1792,24 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
||||
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
|
||||
LOG_DEBUG("Update DB node 0x%x, rx_time=%u", mp.from, mp.rx_time);
|
||||
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(getFrom(&mp));
|
||||
if (!info) {
|
||||
meshtastic_NodeDetail *detail = getOrCreateMeshNode(getFrom(&mp));
|
||||
if (!detail) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mp.rx_time) // if the packet has a valid timestamp use it to update our last_heard
|
||||
info->last_heard = mp.rx_time;
|
||||
detail->last_heard = mp.rx_time;
|
||||
|
||||
if (mp.rx_snr)
|
||||
info->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
|
||||
detail->snr = mp.rx_snr; // keep the most recent SNR we received for this node.
|
||||
|
||||
info->via_mqtt = mp.via_mqtt; // Store if we received this packet via MQTT
|
||||
detailSetFlag(*detail, NODEDETAIL_FLAG_VIA_MQTT, mp.via_mqtt); // Store if we received this packet via MQTT
|
||||
|
||||
// If hopStart was set and there wasn't someone messing with the limit in the middle, add hopsAway
|
||||
if (mp.hop_start != 0 && mp.hop_limit <= mp.hop_start) {
|
||||
info->has_hops_away = true;
|
||||
info->hops_away = mp.hop_start - mp.hop_limit;
|
||||
detailSetFlag(*detail, NODEDETAIL_FLAG_HAS_HOPS_AWAY, true);
|
||||
uint32_t hopDelta = mp.hop_start - mp.hop_limit;
|
||||
detail->hops_away = static_cast<uint8_t>(std::min<uint32_t>(hopDelta, UINT8_MAX));
|
||||
}
|
||||
sortMeshDB();
|
||||
}
|
||||
@@ -1801,9 +1817,9 @@ void NodeDB::updateFrom(const meshtastic_MeshPacket &mp)
|
||||
|
||||
void NodeDB::set_favorite(bool is_favorite, uint32_t nodeId)
|
||||
{
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(nodeId);
|
||||
if (lite && lite->is_favorite != is_favorite) {
|
||||
lite->is_favorite = is_favorite;
|
||||
meshtastic_NodeDetail *detail = getMeshNode(nodeId);
|
||||
if (detail && detailIsFavorite(*detail) != is_favorite) {
|
||||
detailSetFlag(*detail, NODEDETAIL_FLAG_IS_FAVORITE, is_favorite);
|
||||
sortMeshDB();
|
||||
saveNodeDatabaseToDisk();
|
||||
}
|
||||
@@ -1817,10 +1833,10 @@ bool NodeDB::isFavorite(uint32_t nodeId)
|
||||
if (nodeId == NODENUM_BROADCAST)
|
||||
return false;
|
||||
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(nodeId);
|
||||
meshtastic_NodeDetail *detail = getMeshNode(nodeId);
|
||||
|
||||
if (lite) {
|
||||
return lite->is_favorite;
|
||||
if (detail) {
|
||||
return detailIsFavorite(*detail);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1836,23 +1852,21 @@ bool NodeDB::isFromOrToFavoritedNode(const meshtastic_MeshPacket &p)
|
||||
if (p.to == NODENUM_BROADCAST)
|
||||
return isFavorite(p.from); // we never store NODENUM_BROADCAST in the DB, so we only need to check p.from
|
||||
|
||||
meshtastic_NodeInfoLite *lite = NULL;
|
||||
|
||||
bool seenFrom = false;
|
||||
bool seenTo = false;
|
||||
|
||||
for (int i = 0; i < numMeshNodes; i++) {
|
||||
lite = &meshNodes->at(i);
|
||||
auto *detail = &meshNodes->at(i);
|
||||
|
||||
if (lite->num == p.from) {
|
||||
if (lite->is_favorite)
|
||||
if (detail->num == p.from) {
|
||||
if (detailIsFavorite(*detail))
|
||||
return true;
|
||||
|
||||
seenFrom = true;
|
||||
}
|
||||
|
||||
if (lite->num == p.to) {
|
||||
if (lite->is_favorite)
|
||||
if (detail->num == p.to) {
|
||||
if (detailIsFavorite(*detail))
|
||||
return true;
|
||||
|
||||
seenTo = true;
|
||||
@@ -1888,10 +1902,10 @@ void NodeDB::sortMeshDB()
|
||||
// TODO: Look for at(i-1) also matching own node num, and throw the DB in the trash
|
||||
std::swap(meshNodes->at(i), meshNodes->at(i - 1));
|
||||
changed = true;
|
||||
} else if (meshNodes->at(i).is_favorite && !meshNodes->at(i - 1).is_favorite) {
|
||||
} else if (detailIsFavorite(meshNodes->at(i)) && !detailIsFavorite(meshNodes->at(i - 1))) {
|
||||
std::swap(meshNodes->at(i), meshNodes->at(i - 1));
|
||||
changed = true;
|
||||
} else if (!meshNodes->at(i).is_favorite && meshNodes->at(i - 1).is_favorite) {
|
||||
} else if (!detailIsFavorite(meshNodes->at(i)) && detailIsFavorite(meshNodes->at(i - 1))) {
|
||||
// noop
|
||||
} else if (meshNodes->at(i).last_heard > meshNodes->at(i - 1).last_heard) {
|
||||
std::swap(meshNodes->at(i), meshNodes->at(i - 1));
|
||||
@@ -1905,11 +1919,11 @@ void NodeDB::sortMeshDB()
|
||||
|
||||
uint8_t NodeDB::getMeshNodeChannel(NodeNum n)
|
||||
{
|
||||
const meshtastic_NodeInfoLite *info = getMeshNode(n);
|
||||
if (!info) {
|
||||
const meshtastic_NodeDetail *detail = getMeshNode(n);
|
||||
if (!detail) {
|
||||
return 0; // defaults to PRIMARY
|
||||
}
|
||||
return info->channel;
|
||||
return detail->channel;
|
||||
}
|
||||
|
||||
std::string NodeDB::getNodeId() const
|
||||
@@ -1921,7 +1935,7 @@ std::string NodeDB::getNodeId() const
|
||||
|
||||
/// Find a node in our DB, return null for missing
|
||||
/// NOTE: This function might be called from an ISR
|
||||
meshtastic_NodeInfoLite *NodeDB::getMeshNode(NodeNum n)
|
||||
meshtastic_NodeDetail *NodeDB::getMeshNode(NodeNum n)
|
||||
{
|
||||
for (int i = 0; i < numMeshNodes; i++)
|
||||
if (meshNodes->at(i).num == n)
|
||||
@@ -1937,11 +1951,11 @@ bool NodeDB::isFull()
|
||||
}
|
||||
|
||||
/// Find a node in our DB, create an empty NodeInfo if missing
|
||||
meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
meshtastic_NodeDetail *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
{
|
||||
meshtastic_NodeInfoLite *lite = getMeshNode(n);
|
||||
meshtastic_NodeDetail *detail = getMeshNode(n);
|
||||
|
||||
if (!lite) {
|
||||
if (!detail) {
|
||||
if (isFull()) {
|
||||
LOG_INFO("Node database full with %i nodes and %u bytes free. Erasing oldest entry", numMeshNodes,
|
||||
memGet.getFreeHeap());
|
||||
@@ -1952,14 +1966,14 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
int oldestBoringIndex = -1;
|
||||
for (int i = 1; i < numMeshNodes; i++) {
|
||||
// Simply the oldest non-favorite, non-ignored, non-verified node
|
||||
if (!meshNodes->at(i).is_favorite && !meshNodes->at(i).is_ignored &&
|
||||
!(meshNodes->at(i).bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) &&
|
||||
meshNodes->at(i).last_heard < oldest) {
|
||||
const auto &candidate = meshNodes->at(i);
|
||||
if (!detailIsFavorite(candidate) && !detailIsIgnored(candidate) &&
|
||||
!detailHasFlag(candidate, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED) && meshNodes->at(i).last_heard < oldest) {
|
||||
oldest = meshNodes->at(i).last_heard;
|
||||
oldestIndex = i;
|
||||
}
|
||||
// The oldest "boring" node
|
||||
if (!meshNodes->at(i).is_favorite && !meshNodes->at(i).is_ignored && meshNodes->at(i).user.public_key.size == 0 &&
|
||||
if (!detailIsFavorite(candidate) && !detailIsIgnored(candidate) && candidate.public_key.size == 0 &&
|
||||
meshNodes->at(i).last_heard < oldestBoring) {
|
||||
oldestBoring = meshNodes->at(i).last_heard;
|
||||
oldestBoringIndex = i;
|
||||
@@ -1979,33 +1993,33 @@ meshtastic_NodeInfoLite *NodeDB::getOrCreateMeshNode(NodeNum n)
|
||||
}
|
||||
}
|
||||
// add the node at the end
|
||||
lite = &meshNodes->at((numMeshNodes)++);
|
||||
detail = &meshNodes->at((numMeshNodes)++);
|
||||
|
||||
// everything is missing except the nodenum
|
||||
memset(lite, 0, sizeof(*lite));
|
||||
lite->num = n;
|
||||
*detail = makeDefaultDetail();
|
||||
detail->num = n;
|
||||
LOG_INFO("Adding node to database with %i nodes and %u bytes free!", numMeshNodes, memGet.getFreeHeap());
|
||||
}
|
||||
|
||||
return lite;
|
||||
return detail;
|
||||
}
|
||||
|
||||
/// Sometimes we will have Position objects that only have a time, so check for
|
||||
/// valid lat/lon
|
||||
bool NodeDB::hasValidPosition(const meshtastic_NodeInfoLite *n)
|
||||
bool NodeDB::hasValidPosition(const meshtastic_NodeDetail *n)
|
||||
{
|
||||
return n->has_position && (n->position.latitude_i != 0 || n->position.longitude_i != 0);
|
||||
return detailHasFlag(*n, NODEDETAIL_FLAG_HAS_POSITION) && (n->latitude_i != 0 || n->longitude_i != 0);
|
||||
}
|
||||
|
||||
/// If we have a node / user and they report is_licensed = true
|
||||
/// we consider them licensed
|
||||
UserLicenseStatus NodeDB::getLicenseStatus(uint32_t nodeNum)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = getMeshNode(nodeNum);
|
||||
if (!info || !info->has_user) {
|
||||
meshtastic_NodeDetail *detail = getMeshNode(nodeNum);
|
||||
if (!detail || !detailHasFlag(*detail, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
return UserLicenseStatus::NotKnown;
|
||||
}
|
||||
return info->user.is_licensed ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed;
|
||||
return detailHasFlag(*detail, NODEDETAIL_FLAG_IS_LICENSED) ? UserLicenseStatus::Licensed : UserLicenseStatus::NotLicensed;
|
||||
}
|
||||
|
||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include <Arduino.h>
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <pb_encode.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -104,8 +106,13 @@ static constexpr const char *moduleConfigFileName = "/prefs/module.proto";
|
||||
static constexpr const char *channelFileName = "/prefs/channels.proto";
|
||||
static constexpr const char *backupFileName = "/backups/backup.proto";
|
||||
|
||||
template <typename T> inline T clampValue(T value, T low, T high)
|
||||
{
|
||||
return std::max(low, std::min(value, high));
|
||||
}
|
||||
|
||||
/// Given a node, return how many seconds in the past (vs now) that we last heard from it
|
||||
uint32_t sinceLastSeen(const meshtastic_NodeInfoLite *n);
|
||||
uint32_t sinceLastSeen(const meshtastic_NodeDetail *n);
|
||||
|
||||
/// Given a packet, return how many seconds in the past (vs now) it was received
|
||||
uint32_t sinceReceived(const meshtastic_MeshPacket *p);
|
||||
@@ -135,9 +142,9 @@ class NodeDB
|
||||
// Note: these two references just point into our static array we serialize to/from disk
|
||||
|
||||
public:
|
||||
std::vector<meshtastic_NodeInfoLite> *meshNodes;
|
||||
std::vector<meshtastic_NodeDetail> *meshNodes;
|
||||
bool updateGUI = false; // we think the gui should definitely be redrawn, screen will clear this once handled
|
||||
meshtastic_NodeInfoLite *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
|
||||
meshtastic_NodeDetail *updateGUIforNode = NULL; // if currently showing this node, we think you should update the GUI
|
||||
Observable<const meshtastic::NodeStatus *> newStatus;
|
||||
pb_size_t numMeshNodes;
|
||||
|
||||
@@ -241,15 +248,15 @@ class NodeDB
|
||||
|
||||
void installRoleDefaults(meshtastic_Config_DeviceConfig_Role role);
|
||||
|
||||
const meshtastic_NodeInfoLite *readNextMeshNode(uint32_t &readIndex);
|
||||
const meshtastic_NodeDetail *readNextMeshNode(uint32_t &readIndex);
|
||||
|
||||
meshtastic_NodeInfoLite *getMeshNodeByIndex(size_t x)
|
||||
meshtastic_NodeDetail *getMeshNodeByIndex(size_t x)
|
||||
{
|
||||
assert(x < numMeshNodes);
|
||||
return &meshNodes->at(x);
|
||||
}
|
||||
|
||||
virtual meshtastic_NodeInfoLite *getMeshNode(NodeNum n);
|
||||
virtual meshtastic_NodeDetail *getMeshNode(NodeNum n);
|
||||
size_t getNumMeshNodes() { return numMeshNodes; }
|
||||
|
||||
UserLicenseStatus getLicenseStatus(uint32_t nodeNum);
|
||||
@@ -260,7 +267,7 @@ class NodeDB
|
||||
emptyNodeDatabase.version = DEVICESTATE_CUR_VER;
|
||||
size_t nodeDatabaseSize;
|
||||
pb_get_encoded_size(&nodeDatabaseSize, meshtastic_NodeDatabase_fields, &emptyNodeDatabase);
|
||||
return nodeDatabaseSize + (MAX_NUM_NODES * meshtastic_NodeInfoLite_size);
|
||||
return nodeDatabaseSize + (MAX_NUM_NODES * meshtastic_NodeDetail_size);
|
||||
}
|
||||
|
||||
// returns true if the maximum number of nodes is reached or we are running low on memory
|
||||
@@ -281,7 +288,7 @@ class NodeDB
|
||||
localPosition = position;
|
||||
}
|
||||
|
||||
bool hasValidPosition(const meshtastic_NodeInfoLite *n);
|
||||
bool hasValidPosition(const meshtastic_NodeDetail *n);
|
||||
|
||||
#if !defined(MESHTASTIC_EXCLUDE_PKI)
|
||||
bool checkLowEntropyPublicKey(const meshtastic_Config_SecurityConfig_public_key_t &keyToTest);
|
||||
@@ -304,8 +311,8 @@ class NodeDB
|
||||
uint32_t lastNodeDbSave = 0; // when we last saved our db to flash
|
||||
uint32_t lastBackupAttempt = 0; // when we last tried a backup automatically or manually
|
||||
uint32_t lastSort = 0; // When last sorted the nodeDB
|
||||
/// Find a node in our DB, create an empty NodeInfoLite if missing
|
||||
meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n);
|
||||
/// Find a node in our DB, create an empty NodeDetail if missing
|
||||
meshtastic_NodeDetail *getOrCreateMeshNode(NodeNum n);
|
||||
|
||||
/*
|
||||
* Internal boolean to track sorting paused
|
||||
@@ -370,6 +377,262 @@ extern uint32_t error_address;
|
||||
#define NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_SHIFT 0
|
||||
#define NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK (1 << NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_SHIFT)
|
||||
|
||||
enum NodeDetailFlag : uint32_t {
|
||||
NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED = 1u << 0,
|
||||
NODEDETAIL_FLAG_HAS_USER = 1u << 1,
|
||||
NODEDETAIL_FLAG_HAS_POSITION = 1u << 2,
|
||||
NODEDETAIL_FLAG_HAS_DEVICE_METRICS = 1u << 3,
|
||||
NODEDETAIL_FLAG_VIA_MQTT = 1u << 4,
|
||||
NODEDETAIL_FLAG_IS_FAVORITE = 1u << 5,
|
||||
NODEDETAIL_FLAG_IS_IGNORED = 1u << 6,
|
||||
NODEDETAIL_FLAG_HAS_HOPS_AWAY = 1u << 7,
|
||||
NODEDETAIL_FLAG_IS_LICENSED = 1u << 8,
|
||||
NODEDETAIL_FLAG_HAS_UNMESSAGABLE = 1u << 9,
|
||||
NODEDETAIL_FLAG_IS_UNMESSAGABLE = 1u << 10,
|
||||
NODEDETAIL_FLAG_HAS_BATTERY_LEVEL = 1u << 11,
|
||||
NODEDETAIL_FLAG_HAS_VOLTAGE = 1u << 12,
|
||||
NODEDETAIL_FLAG_HAS_CHANNEL_UTIL = 1u << 13,
|
||||
NODEDETAIL_FLAG_HAS_AIR_UTIL_TX = 1u << 14,
|
||||
NODEDETAIL_FLAG_HAS_UPTIME = 1u << 15
|
||||
};
|
||||
|
||||
inline bool detailHasFlag(const meshtastic_NodeDetail &detail, NodeDetailFlag flag)
|
||||
{
|
||||
return (detail.flags & static_cast<uint32_t>(flag)) != 0;
|
||||
}
|
||||
|
||||
inline void detailSetFlag(meshtastic_NodeDetail &detail, NodeDetailFlag flag, bool value = true)
|
||||
{
|
||||
if (value) {
|
||||
detail.flags |= static_cast<uint32_t>(flag);
|
||||
} else {
|
||||
detail.flags &= ~static_cast<uint32_t>(flag);
|
||||
}
|
||||
}
|
||||
|
||||
inline meshtastic_NodeDetail makeDefaultDetail()
|
||||
{
|
||||
meshtastic_NodeDetail detail = meshtastic_NodeDetail_init_default;
|
||||
return detail;
|
||||
}
|
||||
|
||||
inline void clearUserFromDetail(meshtastic_NodeDetail &detail)
|
||||
{
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_USER, false);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_IS_LICENSED, false);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UNMESSAGABLE, false);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_IS_UNMESSAGABLE, false);
|
||||
detail.long_name[0] = '\0';
|
||||
detail.short_name[0] = '\0';
|
||||
memset(detail.macaddr, 0, sizeof(detail.macaddr));
|
||||
detail.hw_model = _meshtastic_HardwareModel_MIN;
|
||||
detail.role = _meshtastic_Config_DeviceConfig_Role_MIN;
|
||||
detail.public_key.size = 0;
|
||||
memset(detail.public_key.bytes, 0, sizeof(detail.public_key.bytes));
|
||||
}
|
||||
|
||||
inline void applyUserLiteToDetail(meshtastic_NodeDetail &detail, const meshtastic_UserLite &userLite)
|
||||
{
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_USER, true);
|
||||
strncpy(detail.long_name, userLite.long_name, sizeof(detail.long_name));
|
||||
detail.long_name[sizeof(detail.long_name) - 1] = '\0';
|
||||
strncpy(detail.short_name, userLite.short_name, sizeof(detail.short_name));
|
||||
detail.short_name[sizeof(detail.short_name) - 1] = '\0';
|
||||
memcpy(detail.macaddr, userLite.macaddr, sizeof(detail.macaddr));
|
||||
detail.hw_model = userLite.hw_model;
|
||||
detail.role = userLite.role;
|
||||
|
||||
const pb_size_t keySize = std::min(userLite.public_key.size, static_cast<pb_size_t>(sizeof(detail.public_key.bytes)));
|
||||
memcpy(detail.public_key.bytes, userLite.public_key.bytes, keySize);
|
||||
detail.public_key.size = keySize;
|
||||
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_IS_LICENSED, userLite.is_licensed);
|
||||
|
||||
if (userLite.has_is_unmessagable) {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UNMESSAGABLE, true);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_IS_UNMESSAGABLE, userLite.is_unmessagable);
|
||||
} else {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UNMESSAGABLE, false);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_IS_UNMESSAGABLE, false);
|
||||
}
|
||||
}
|
||||
|
||||
inline void applyUserToDetail(meshtastic_NodeDetail &detail, const meshtastic_User &user)
|
||||
{
|
||||
meshtastic_UserLite lite = meshtastic_UserLite_init_default;
|
||||
strncpy(lite.long_name, user.long_name, sizeof(lite.long_name));
|
||||
lite.long_name[sizeof(lite.long_name) - 1] = '\0';
|
||||
strncpy(lite.short_name, user.short_name, sizeof(lite.short_name));
|
||||
lite.short_name[sizeof(lite.short_name) - 1] = '\0';
|
||||
lite.hw_model = user.hw_model;
|
||||
lite.role = user.role;
|
||||
lite.is_licensed = user.is_licensed;
|
||||
memcpy(lite.macaddr, user.macaddr, sizeof(lite.macaddr));
|
||||
const pb_size_t keySize = std::min(user.public_key.size, static_cast<pb_size_t>(sizeof(lite.public_key.bytes)));
|
||||
memcpy(lite.public_key.bytes, user.public_key.bytes, keySize);
|
||||
lite.public_key.size = keySize;
|
||||
lite.has_is_unmessagable = user.has_is_unmessagable;
|
||||
lite.is_unmessagable = user.is_unmessagable;
|
||||
applyUserLiteToDetail(detail, lite);
|
||||
}
|
||||
|
||||
inline void clearPositionFromDetail(meshtastic_NodeDetail &detail)
|
||||
{
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_POSITION, false);
|
||||
detail.latitude_i = 0;
|
||||
detail.longitude_i = 0;
|
||||
detail.altitude = 0;
|
||||
detail.position_time = 0;
|
||||
detail.position_source = _meshtastic_Position_LocSource_MIN;
|
||||
}
|
||||
|
||||
inline void applyPositionLiteToDetail(meshtastic_NodeDetail &detail, const meshtastic_PositionLite &positionLite)
|
||||
{
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_POSITION, true);
|
||||
detail.latitude_i = positionLite.latitude_i;
|
||||
detail.longitude_i = positionLite.longitude_i;
|
||||
detail.altitude = positionLite.altitude;
|
||||
detail.position_time = positionLite.time;
|
||||
detail.position_source = positionLite.location_source;
|
||||
}
|
||||
|
||||
inline void applyPositionToDetail(meshtastic_NodeDetail &detail, const meshtastic_Position &position)
|
||||
{
|
||||
meshtastic_PositionLite lite = meshtastic_PositionLite_init_default;
|
||||
lite.latitude_i = position.latitude_i;
|
||||
lite.longitude_i = position.longitude_i;
|
||||
lite.altitude = position.altitude;
|
||||
lite.location_source = position.location_source;
|
||||
lite.time = position.time;
|
||||
applyPositionLiteToDetail(detail, lite);
|
||||
}
|
||||
|
||||
inline void clearMetricsFromDetail(meshtastic_NodeDetail &detail)
|
||||
{
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_DEVICE_METRICS, false);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_BATTERY_LEVEL, false);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_VOLTAGE, false);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_CHANNEL_UTIL, false);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_AIR_UTIL_TX, false);
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UPTIME, false);
|
||||
detail.battery_level = 0;
|
||||
detail.voltage_millivolts = 0;
|
||||
detail.channel_utilization_permille = 0;
|
||||
detail.air_util_tx_permille = 0;
|
||||
detail.uptime_seconds = 0;
|
||||
}
|
||||
|
||||
inline void applyMetricsToDetail(meshtastic_NodeDetail &detail, const meshtastic_DeviceMetrics &metrics)
|
||||
{
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_DEVICE_METRICS, true);
|
||||
|
||||
if (metrics.has_battery_level) {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_BATTERY_LEVEL, true);
|
||||
uint32_t battery = metrics.battery_level;
|
||||
if (battery > 255u) {
|
||||
battery = 255u;
|
||||
}
|
||||
detail.battery_level = static_cast<uint8_t>(battery);
|
||||
} else {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_BATTERY_LEVEL, false);
|
||||
}
|
||||
|
||||
if (metrics.has_voltage) {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_VOLTAGE, true);
|
||||
double limitedVoltage = clampValue(static_cast<double>(metrics.voltage), 0.0, 65.535);
|
||||
int millivolts = static_cast<int>(std::lround(limitedVoltage * 1000.0));
|
||||
millivolts = clampValue<int>(millivolts, 0, 0xFFFF);
|
||||
detail.voltage_millivolts = static_cast<uint16_t>(millivolts);
|
||||
} else {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_VOLTAGE, false);
|
||||
detail.voltage_millivolts = 0;
|
||||
}
|
||||
|
||||
if (metrics.has_channel_utilization) {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_CHANNEL_UTIL, true);
|
||||
double limitedUtil = clampValue(static_cast<double>(metrics.channel_utilization), 0.0, 100.0);
|
||||
int permille = static_cast<int>(std::lround(limitedUtil * 10.0));
|
||||
permille = clampValue<int>(permille, 0, 1000);
|
||||
detail.channel_utilization_permille = static_cast<uint16_t>(permille);
|
||||
} else {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_CHANNEL_UTIL, false);
|
||||
detail.channel_utilization_permille = 0;
|
||||
}
|
||||
|
||||
if (metrics.has_air_util_tx) {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_AIR_UTIL_TX, true);
|
||||
double limitedAirUtil = clampValue(static_cast<double>(metrics.air_util_tx), 0.0, 100.0);
|
||||
int permille = static_cast<int>(std::lround(limitedAirUtil * 10.0));
|
||||
permille = clampValue<int>(permille, 0, 1000);
|
||||
detail.air_util_tx_permille = static_cast<uint16_t>(permille);
|
||||
} else {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_AIR_UTIL_TX, false);
|
||||
detail.air_util_tx_permille = 0;
|
||||
}
|
||||
|
||||
if (metrics.has_uptime_seconds) {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UPTIME, true);
|
||||
detail.uptime_seconds = metrics.uptime_seconds;
|
||||
} else {
|
||||
detailSetFlag(detail, NODEDETAIL_FLAG_HAS_UPTIME, false);
|
||||
detail.uptime_seconds = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool detailIsFavorite(const meshtastic_NodeDetail &detail)
|
||||
{
|
||||
return detailHasFlag(detail, NODEDETAIL_FLAG_IS_FAVORITE);
|
||||
}
|
||||
|
||||
inline bool detailIsIgnored(const meshtastic_NodeDetail &detail)
|
||||
{
|
||||
return detailHasFlag(detail, NODEDETAIL_FLAG_IS_IGNORED);
|
||||
}
|
||||
|
||||
inline bool detailViaMqtt(const meshtastic_NodeDetail &detail)
|
||||
{
|
||||
return detailHasFlag(detail, NODEDETAIL_FLAG_VIA_MQTT);
|
||||
}
|
||||
|
||||
inline meshtastic_PositionLite detailToPositionLite(const meshtastic_NodeDetail &detail)
|
||||
{
|
||||
meshtastic_PositionLite lite = meshtastic_PositionLite_init_default;
|
||||
if (!detailHasFlag(detail, NODEDETAIL_FLAG_HAS_POSITION)) {
|
||||
return lite;
|
||||
}
|
||||
|
||||
lite.latitude_i = detail.latitude_i;
|
||||
lite.longitude_i = detail.longitude_i;
|
||||
lite.altitude = detail.altitude;
|
||||
lite.time = detail.position_time;
|
||||
lite.location_source = detail.position_source;
|
||||
return lite;
|
||||
}
|
||||
|
||||
inline meshtastic_UserLite detailToUserLite(const meshtastic_NodeDetail &detail)
|
||||
{
|
||||
meshtastic_UserLite lite = meshtastic_UserLite_init_default;
|
||||
if (!detailHasFlag(detail, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
return lite;
|
||||
}
|
||||
|
||||
strncpy(lite.long_name, detail.long_name, sizeof(lite.long_name));
|
||||
lite.long_name[sizeof(lite.long_name) - 1] = '\0';
|
||||
strncpy(lite.short_name, detail.short_name, sizeof(lite.short_name));
|
||||
lite.short_name[sizeof(lite.short_name) - 1] = '\0';
|
||||
lite.hw_model = detail.hw_model;
|
||||
lite.role = detail.role;
|
||||
lite.is_licensed = detailHasFlag(detail, NODEDETAIL_FLAG_IS_LICENSED);
|
||||
memcpy(lite.macaddr, detail.macaddr, sizeof(lite.macaddr));
|
||||
lite.public_key.size = std::min(static_cast<pb_size_t>(sizeof(lite.public_key.bytes)), detail.public_key.size);
|
||||
memcpy(lite.public_key.bytes, detail.public_key.bytes, lite.public_key.size);
|
||||
if (detailHasFlag(detail, NODEDETAIL_FLAG_HAS_UNMESSAGABLE)) {
|
||||
lite.has_is_unmessagable = true;
|
||||
lite.is_unmessagable = detailHasFlag(detail, NODEDETAIL_FLAG_IS_UNMESSAGABLE);
|
||||
}
|
||||
return lite;
|
||||
}
|
||||
|
||||
#define Module_Config_size \
|
||||
(ModuleConfig_CannedMessageConfig_size + ModuleConfig_ExternalNotificationConfig_size + ModuleConfig_MQTTConfig_size + \
|
||||
ModuleConfig_RangeTestConfig_size + ModuleConfig_SerialConfig_size + ModuleConfig_StoreForwardConfig_size + \
|
||||
|
||||
@@ -267,9 +267,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
|
||||
case STATE_SEND_OWN_NODEINFO: {
|
||||
LOG_DEBUG("Send My NodeInfo");
|
||||
auto us = nodeDB->readNextMeshNode(readIndex);
|
||||
const meshtastic_NodeDetail *us = nodeDB->readNextMeshNode(readIndex);
|
||||
if (us) {
|
||||
auto info = TypeConversions::ConvertToNodeInfo(us);
|
||||
auto info = TypeConversions::ConvertToNodeInfo(*us);
|
||||
info.has_hops_away = false;
|
||||
info.is_favorite = true;
|
||||
{
|
||||
@@ -633,11 +633,11 @@ void PhoneAPI::prefetchNodeInfos()
|
||||
{
|
||||
concurrency::LockGuard guard(&nodeInfoMutex);
|
||||
while (nodeInfoQueue.size() < kNodePrefetchDepth) {
|
||||
auto nextNode = nodeDB->readNextMeshNode(readIndex);
|
||||
const meshtastic_NodeDetail *nextNode = nodeDB->readNextMeshNode(readIndex);
|
||||
if (!nextNode)
|
||||
break;
|
||||
|
||||
auto info = TypeConversions::ConvertToNodeInfo(nextNode);
|
||||
auto info = TypeConversions::ConvertToNodeInfo(*nextNode);
|
||||
bool isUs = info.num == nodeDB->getNodeNum();
|
||||
info.hops_away = isUs ? 0 : info.hops_away;
|
||||
info.last_heard = isUs ? getValidTime(RTCQualityFromNet) : info.last_heard;
|
||||
|
||||
@@ -58,7 +58,7 @@ template <class T> class ProtobufModule : protected SinglePortModule
|
||||
const char *getSenderShortName(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
auto node = nodeDB->getMeshNode(getFrom(&mp));
|
||||
const char *sender = (node) ? node->user.short_name : "???";
|
||||
const char *sender = (node) ? node->short_name : "???";
|
||||
return sender;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,11 +119,16 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
|
||||
// stop the immediate relayer's retransmissions.
|
||||
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, p->channel, 0);
|
||||
}
|
||||
} else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0 &&
|
||||
(nodeDB->getMeshNode(p->from) == nullptr || nodeDB->getMeshNode(p->from)->user.public_key.size == 0)) {
|
||||
LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY");
|
||||
sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(),
|
||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
|
||||
} else if (p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && p->channel == 0) {
|
||||
const meshtastic_NodeDetail *fromDetail = nodeDB->getMeshNode(p->from);
|
||||
if (!fromDetail || fromDetail->public_key.size == 0) {
|
||||
LOG_INFO("PKI packet from unknown node, send PKI_UNKNOWN_PUBKEY");
|
||||
sendAckNak(meshtastic_Routing_Error_PKI_UNKNOWN_PUBKEY, getFrom(p), p->id, channels.getPrimaryIndex(),
|
||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
|
||||
} else {
|
||||
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(),
|
||||
routingModule->getHopLimitForResponse(p->hop_start, p->hop_limit));
|
||||
}
|
||||
} else {
|
||||
// Send a 'NO_CHANNEL' error on the primary channel if want_ack packet destined for us cannot be decoded
|
||||
sendAckNak(meshtastic_Routing_Error_NO_CHANNEL, getFrom(p), p->id, channels.getPrimaryIndex(),
|
||||
|
||||
@@ -100,21 +100,20 @@ bool Router::shouldDecrementHopLimit(const meshtastic_MeshPacket *p)
|
||||
// Optimized search for favorite routers with matching last byte
|
||||
// Check ordering optimized for IoT devices (cheapest checks first)
|
||||
for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||
const meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i);
|
||||
if (!node)
|
||||
continue;
|
||||
|
||||
// Check 1: is_favorite (cheapest - single bool)
|
||||
if (!node->is_favorite)
|
||||
if (!detailIsFavorite(*node))
|
||||
continue;
|
||||
|
||||
// Check 2: has_user (cheap - single bool)
|
||||
if (!node->has_user)
|
||||
if (!detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER))
|
||||
continue;
|
||||
|
||||
// Check 3: role check (moderate cost - multiple comparisons)
|
||||
if (!IS_ONE_OF(node->user.role, meshtastic_Config_DeviceConfig_Role_ROUTER,
|
||||
meshtastic_Config_DeviceConfig_Role_ROUTER_LATE)) {
|
||||
if (!IS_ONE_OF(node->role, meshtastic_Config_DeviceConfig_Role_ROUTER, meshtastic_Config_DeviceConfig_Role_ROUTER_LATE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -261,7 +260,7 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
|
||||
|
||||
// don't override if a channel was requested and no need to set it when PKI is enforced
|
||||
if (!p->channel && !p->pki_encrypted && !isBroadcast(p->to)) {
|
||||
meshtastic_NodeInfoLite const *node = nodeDB->getMeshNode(p->to);
|
||||
const meshtastic_NodeDetail *node = nodeDB->getMeshNode(p->to);
|
||||
if (node) {
|
||||
p->channel = node->channel;
|
||||
LOG_DEBUG("localSend to channel %d", p->channel);
|
||||
@@ -409,8 +408,11 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
||||
{
|
||||
concurrency::LockGuard g(cryptLock);
|
||||
|
||||
const meshtastic_NodeDetail *fromDetail = nodeDB->getMeshNode(p->from);
|
||||
const meshtastic_NodeDetail *toDetail = nodeDB->getMeshNode(p->to);
|
||||
|
||||
if (config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY &&
|
||||
(nodeDB->getMeshNode(p->from) == NULL || !nodeDB->getMeshNode(p->from)->has_user)) {
|
||||
(!fromDetail || !detailHasFlag(*fromDetail, NODEDETAIL_FLAG_HAS_USER))) {
|
||||
LOG_DEBUG("Node 0x%x not in nodeDB-> Rebroadcast mode KNOWN_ONLY will ignore packet", p->from);
|
||||
return DecodeState::DECODE_FAILURE;
|
||||
}
|
||||
@@ -427,33 +429,38 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
||||
ChannelIndex chIndex = 0;
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
// Attempt PKI decryption first
|
||||
if (p->channel == 0 && isToUs(p) && p->to > 0 && !isBroadcast(p->to) && nodeDB->getMeshNode(p->from) != nullptr &&
|
||||
nodeDB->getMeshNode(p->from)->user.public_key.size > 0 && nodeDB->getMeshNode(p->to)->user.public_key.size > 0 &&
|
||||
if (p->channel == 0 && isToUs(p) && p->to > 0 && !isBroadcast(p->to) && fromDetail != nullptr && toDetail != nullptr &&
|
||||
rawSize > MESHTASTIC_PKC_OVERHEAD) {
|
||||
LOG_DEBUG("Attempt PKI decryption");
|
||||
meshtastic_UserLite fromLite = meshtastic_UserLite_init_default;
|
||||
meshtastic_UserLite toLite = meshtastic_UserLite_init_default;
|
||||
fromLite = detailToUserLite(*fromDetail);
|
||||
toLite = detailToUserLite(*toDetail);
|
||||
|
||||
if (crypto->decryptCurve25519(p->from, nodeDB->getMeshNode(p->from)->user.public_key, p->id, rawSize, p->encrypted.bytes,
|
||||
bytes)) {
|
||||
LOG_INFO("PKI Decryption worked!");
|
||||
if (fromLite.public_key.size == 32 && toLite.public_key.size == 32) {
|
||||
LOG_DEBUG("Attempt PKI decryption");
|
||||
|
||||
meshtastic_Data decodedtmp;
|
||||
memset(&decodedtmp, 0, sizeof(decodedtmp));
|
||||
rawSize -= MESHTASTIC_PKC_OVERHEAD;
|
||||
if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &decodedtmp) &&
|
||||
decodedtmp.portnum != meshtastic_PortNum_UNKNOWN_APP) {
|
||||
decrypted = true;
|
||||
LOG_INFO("Packet decrypted using PKI!");
|
||||
p->pki_encrypted = true;
|
||||
memcpy(&p->public_key.bytes, nodeDB->getMeshNode(p->from)->user.public_key.bytes, 32);
|
||||
p->public_key.size = 32;
|
||||
p->decoded = decodedtmp;
|
||||
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
|
||||
if (crypto->decryptCurve25519(p->from, fromLite.public_key, p->id, rawSize, p->encrypted.bytes, bytes)) {
|
||||
LOG_INFO("PKI Decryption worked!");
|
||||
|
||||
meshtastic_Data decodedtmp;
|
||||
memset(&decodedtmp, 0, sizeof(decodedtmp));
|
||||
rawSize -= MESHTASTIC_PKC_OVERHEAD;
|
||||
if (pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &decodedtmp) &&
|
||||
decodedtmp.portnum != meshtastic_PortNum_UNKNOWN_APP) {
|
||||
decrypted = true;
|
||||
LOG_INFO("Packet decrypted using PKI!");
|
||||
p->pki_encrypted = true;
|
||||
memcpy(&p->public_key.bytes, fromLite.public_key.bytes, 32);
|
||||
p->public_key.size = 32;
|
||||
p->decoded = decodedtmp;
|
||||
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
|
||||
} else {
|
||||
LOG_ERROR("PKC Decrypted, but pb_decode failed!");
|
||||
return DecodeState::DECODE_FAILURE;
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR("PKC Decrypted, but pb_decode failed!");
|
||||
return DecodeState::DECODE_FAILURE;
|
||||
LOG_WARN("PKC decrypt attempted but failed!");
|
||||
}
|
||||
} else {
|
||||
LOG_WARN("PKC decrypt attempted but failed!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -596,7 +603,11 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(p->to);
|
||||
const meshtastic_NodeDetail *node = nodeDB->getMeshNode(p->to);
|
||||
meshtastic_UserLite destLite = meshtastic_UserLite_init_default;
|
||||
if (node) {
|
||||
destLite = detailToUserLite(*node);
|
||||
}
|
||||
// We may want to retool things so we can send a PKC packet when the client specifies a key and nodenum, even if the node
|
||||
// is not in the local nodedb
|
||||
// First, only PKC encrypt packets we are originating
|
||||
@@ -613,7 +624,7 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
// Check for valid keys and single node destination
|
||||
config.security.private_key.size == 32 && !isBroadcast(p->to) && node != nullptr &&
|
||||
// Check for a known public key for the destination
|
||||
(node->user.public_key.size == 32) &&
|
||||
(destLite.public_key.size == 32) &&
|
||||
// Some portnums either make no sense to send with PKC
|
||||
p->decoded.portnum != meshtastic_PortNum_TRACEROUTE_APP && p->decoded.portnum != meshtastic_PortNum_NODEINFO_APP &&
|
||||
p->decoded.portnum != meshtastic_PortNum_ROUTING_APP && p->decoded.portnum != meshtastic_PortNum_POSITION_APP) {
|
||||
@@ -621,12 +632,12 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
|
||||
if (numbytes + MESHTASTIC_HEADER_LENGTH + MESHTASTIC_PKC_OVERHEAD > MAX_LORA_PAYLOAD_LEN)
|
||||
return meshtastic_Routing_Error_TOO_LARGE;
|
||||
if (p->pki_encrypted && !memfll(p->public_key.bytes, 0, 32) &&
|
||||
memcmp(p->public_key.bytes, node->user.public_key.bytes, 32) != 0) {
|
||||
memcmp(p->public_key.bytes, destLite.public_key.bytes, 32) != 0) {
|
||||
LOG_WARN("Client public key differs from requested: 0x%02x, stored key begins 0x%02x", *p->public_key.bytes,
|
||||
*node->user.public_key.bytes);
|
||||
*destLite.public_key.bytes);
|
||||
return meshtastic_Routing_Error_PKI_FAILED;
|
||||
}
|
||||
crypto->encryptCurve25519(p->to, getFrom(p), node->user.public_key, p->id, numbytes, bytes, p->encrypted.bytes);
|
||||
crypto->encryptCurve25519(p->to, getFrom(p), destLite.public_key, p->id, numbytes, bytes, p->encrypted.bytes);
|
||||
numbytes += MESHTASTIC_PKC_OVERHEAD;
|
||||
p->channel = 0;
|
||||
p->pki_encrypted = true;
|
||||
@@ -776,8 +787,8 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
|
||||
return;
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite const *node = nodeDB->getMeshNode(p->from);
|
||||
if (node != NULL && node->is_ignored) {
|
||||
const meshtastic_NodeDetail *node = nodeDB->getMeshNode(p->from);
|
||||
if (node != NULL && detailIsIgnored(*node)) {
|
||||
LOG_DEBUG("Ignore msg, 0x%x is ignored", p->from);
|
||||
packetPool.release(p);
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "TypeConversions.h"
|
||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite)
|
||||
{
|
||||
@@ -45,6 +48,196 @@ meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfo
|
||||
return info;
|
||||
}
|
||||
|
||||
meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeDetail &detail)
|
||||
{
|
||||
meshtastic_NodeInfo info = meshtastic_NodeInfo_init_default;
|
||||
|
||||
info.num = detail.num;
|
||||
info.snr = detail.snr;
|
||||
info.last_heard = detail.last_heard;
|
||||
info.channel = detail.channel;
|
||||
info.via_mqtt = detail.flags & NODEDETAIL_FLAG_VIA_MQTT;
|
||||
info.is_favorite = detail.flags & NODEDETAIL_FLAG_IS_FAVORITE;
|
||||
info.is_ignored = detail.flags & NODEDETAIL_FLAG_IS_IGNORED;
|
||||
info.is_key_manually_verified = detail.flags & NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED;
|
||||
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_HOPS_AWAY) {
|
||||
info.has_hops_away = true;
|
||||
info.hops_away = detail.hops_away;
|
||||
}
|
||||
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_POSITION) {
|
||||
info.has_position = true;
|
||||
info.position = meshtastic_Position_init_default;
|
||||
if (detail.latitude_i != 0) {
|
||||
info.position.has_latitude_i = true;
|
||||
}
|
||||
info.position.latitude_i = detail.latitude_i;
|
||||
if (detail.longitude_i != 0) {
|
||||
info.position.has_longitude_i = true;
|
||||
}
|
||||
info.position.longitude_i = detail.longitude_i;
|
||||
if (detail.altitude != 0) {
|
||||
info.position.has_altitude = true;
|
||||
}
|
||||
info.position.altitude = detail.altitude;
|
||||
info.position.location_source = detail.position_source;
|
||||
info.position.time = detail.position_time;
|
||||
}
|
||||
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_USER) {
|
||||
info.has_user = true;
|
||||
meshtastic_User user = meshtastic_User_init_default;
|
||||
snprintf(user.id, sizeof(user.id), "!%08x", detail.num);
|
||||
strncpy(user.long_name, detail.long_name, sizeof(user.long_name));
|
||||
user.long_name[sizeof(user.long_name) - 1] = '\0';
|
||||
strncpy(user.short_name, detail.short_name, sizeof(user.short_name));
|
||||
user.short_name[sizeof(user.short_name) - 1] = '\0';
|
||||
user.hw_model = detail.hw_model;
|
||||
user.role = detail.role;
|
||||
user.is_licensed = detail.flags & NODEDETAIL_FLAG_IS_LICENSED;
|
||||
memcpy(user.macaddr, detail.macaddr, sizeof(user.macaddr));
|
||||
const pb_size_t keySize = std::min(detail.public_key.size, static_cast<pb_size_t>(sizeof(user.public_key.bytes)));
|
||||
memcpy(user.public_key.bytes, detail.public_key.bytes, keySize);
|
||||
user.public_key.size = keySize;
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_UNMESSAGABLE) {
|
||||
user.has_is_unmessagable = true;
|
||||
user.is_unmessagable = detail.flags & NODEDETAIL_FLAG_IS_UNMESSAGABLE;
|
||||
}
|
||||
info.user = user;
|
||||
}
|
||||
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_DEVICE_METRICS) {
|
||||
info.has_device_metrics = true;
|
||||
meshtastic_DeviceMetrics metrics = meshtastic_DeviceMetrics_init_default;
|
||||
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_BATTERY_LEVEL) {
|
||||
metrics.has_battery_level = true;
|
||||
metrics.battery_level = detail.battery_level;
|
||||
}
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_VOLTAGE) {
|
||||
metrics.has_voltage = true;
|
||||
metrics.voltage = static_cast<float>(detail.voltage_millivolts) / 1000.0f;
|
||||
}
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_CHANNEL_UTIL) {
|
||||
metrics.has_channel_utilization = true;
|
||||
metrics.channel_utilization = static_cast<float>(detail.channel_utilization_permille) / 10.0f;
|
||||
}
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_AIR_UTIL_TX) {
|
||||
metrics.has_air_util_tx = true;
|
||||
metrics.air_util_tx = static_cast<float>(detail.air_util_tx_permille) / 10.0f;
|
||||
}
|
||||
if (detail.flags & NODEDETAIL_FLAG_HAS_UPTIME) {
|
||||
metrics.has_uptime_seconds = true;
|
||||
metrics.uptime_seconds = detail.uptime_seconds;
|
||||
}
|
||||
|
||||
info.device_metrics = metrics;
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
meshtastic_NodeDetail TypeConversions::ConvertToNodeDetail(const meshtastic_NodeInfoLite &lite)
|
||||
{
|
||||
meshtastic_NodeDetail detail = meshtastic_NodeDetail_init_default;
|
||||
|
||||
detail.num = lite.num;
|
||||
detail.snr = lite.snr;
|
||||
detail.last_heard = lite.last_heard;
|
||||
detail.channel = lite.channel;
|
||||
detail.next_hop = lite.next_hop;
|
||||
if (lite.has_hops_away) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_HOPS_AWAY;
|
||||
detail.hops_away = lite.hops_away;
|
||||
}
|
||||
if (lite.via_mqtt) {
|
||||
detail.flags |= NODEDETAIL_FLAG_VIA_MQTT;
|
||||
}
|
||||
if (lite.is_favorite) {
|
||||
detail.flags |= NODEDETAIL_FLAG_IS_FAVORITE;
|
||||
}
|
||||
if (lite.is_ignored) {
|
||||
detail.flags |= NODEDETAIL_FLAG_IS_IGNORED;
|
||||
}
|
||||
if (lite.bitfield & NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK) {
|
||||
detail.flags |= NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED;
|
||||
}
|
||||
|
||||
if (lite.has_user) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_USER;
|
||||
strncpy(detail.long_name, lite.user.long_name, sizeof(detail.long_name));
|
||||
detail.long_name[sizeof(detail.long_name) - 1] = '\0';
|
||||
strncpy(detail.short_name, lite.user.short_name, sizeof(detail.short_name));
|
||||
detail.short_name[sizeof(detail.short_name) - 1] = '\0';
|
||||
detail.hw_model = lite.user.hw_model;
|
||||
detail.role = lite.user.role;
|
||||
memcpy(detail.macaddr, lite.user.macaddr, sizeof(detail.macaddr));
|
||||
const pb_size_t keySize = std::min(lite.user.public_key.size, static_cast<pb_size_t>(sizeof(detail.public_key.bytes)));
|
||||
memcpy(detail.public_key.bytes, lite.user.public_key.bytes, keySize);
|
||||
detail.public_key.size = keySize;
|
||||
if (lite.user.is_licensed) {
|
||||
detail.flags |= NODEDETAIL_FLAG_IS_LICENSED;
|
||||
}
|
||||
if (lite.user.has_is_unmessagable) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_UNMESSAGABLE;
|
||||
if (lite.user.is_unmessagable) {
|
||||
detail.flags |= NODEDETAIL_FLAG_IS_UNMESSAGABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lite.has_position) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_POSITION;
|
||||
detail.latitude_i = lite.position.latitude_i;
|
||||
detail.longitude_i = lite.position.longitude_i;
|
||||
detail.altitude = lite.position.altitude;
|
||||
detail.position_time = lite.position.time;
|
||||
detail.position_source = lite.position.location_source;
|
||||
}
|
||||
|
||||
if (lite.has_device_metrics) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_DEVICE_METRICS;
|
||||
const meshtastic_DeviceMetrics &metrics = lite.device_metrics;
|
||||
|
||||
if (metrics.has_battery_level) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_BATTERY_LEVEL;
|
||||
uint32_t battery = metrics.battery_level;
|
||||
if (battery > 255u) {
|
||||
battery = 255u;
|
||||
}
|
||||
detail.battery_level = static_cast<uint8_t>(battery);
|
||||
}
|
||||
if (metrics.has_voltage) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_VOLTAGE;
|
||||
double limitedVoltage = clampValue(static_cast<double>(metrics.voltage), 0.0, 65.535);
|
||||
int millivolts = static_cast<int>(std::lround(limitedVoltage * 1000.0));
|
||||
millivolts = clampValue<int>(millivolts, 0, 0xFFFF);
|
||||
detail.voltage_millivolts = static_cast<uint16_t>(millivolts);
|
||||
}
|
||||
if (metrics.has_channel_utilization) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_CHANNEL_UTIL;
|
||||
double limitedUtil = clampValue(static_cast<double>(metrics.channel_utilization), 0.0, 100.0);
|
||||
int permille = static_cast<int>(std::lround(limitedUtil * 10.0));
|
||||
permille = clampValue<int>(permille, 0, 1000);
|
||||
detail.channel_utilization_permille = static_cast<uint16_t>(permille);
|
||||
}
|
||||
if (metrics.has_air_util_tx) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_AIR_UTIL_TX;
|
||||
double limitedAirUtil = clampValue(static_cast<double>(metrics.air_util_tx), 0.0, 100.0);
|
||||
int permille = static_cast<int>(std::lround(limitedAirUtil * 10.0));
|
||||
permille = clampValue<int>(permille, 0, 1000);
|
||||
detail.air_util_tx_permille = static_cast<uint16_t>(permille);
|
||||
}
|
||||
if (metrics.has_uptime_seconds) {
|
||||
detail.flags |= NODEDETAIL_FLAG_HAS_UPTIME;
|
||||
detail.uptime_seconds = metrics.uptime_seconds;
|
||||
}
|
||||
}
|
||||
|
||||
return detail;
|
||||
}
|
||||
|
||||
meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Position position)
|
||||
{
|
||||
meshtastic_PositionLite lite = meshtastic_PositionLite_init_default;
|
||||
|
||||
@@ -8,6 +8,8 @@ class TypeConversions
|
||||
{
|
||||
public:
|
||||
static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite);
|
||||
static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeDetail &detail);
|
||||
static meshtastic_NodeDetail ConvertToNodeDetail(const meshtastic_NodeInfoLite &lite);
|
||||
static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position);
|
||||
static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite);
|
||||
static meshtastic_UserLite ConvertToUserLite(meshtastic_User user);
|
||||
|
||||
@@ -15,6 +15,9 @@ PB_BIND(meshtastic_UserLite, meshtastic_UserLite, AUTO)
|
||||
PB_BIND(meshtastic_NodeInfoLite, meshtastic_NodeInfoLite, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_NodeDetail, meshtastic_NodeDetail, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_DeviceState, meshtastic_DeviceState, 2)
|
||||
|
||||
|
||||
|
||||
@@ -101,6 +101,49 @@ typedef struct _meshtastic_NodeInfoLite {
|
||||
uint32_t bitfield;
|
||||
} meshtastic_NodeInfoLite;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(32) meshtastic_NodeDetail_public_key_t;
|
||||
/* Flattened node representation used for the compact NodeDB rewrite.
|
||||
Uses integer scaling where possible and a single flags bitfield for booleans. */
|
||||
typedef struct _meshtastic_NodeDetail {
|
||||
/* The node number */
|
||||
uint32_t num;
|
||||
/* 48-bit hardware identifier copied from the radio */
|
||||
pb_byte_t macaddr[6];
|
||||
/* Cached long display name */
|
||||
char long_name[40];
|
||||
/* Cached short display name */
|
||||
char short_name[5];
|
||||
/* Hardware model reported by the node */
|
||||
meshtastic_HardwareModel hw_model;
|
||||
/* Role assigned to the node */
|
||||
meshtastic_Config_DeviceConfig_Role role;
|
||||
/* Public key broadcast by the node */
|
||||
meshtastic_NodeDetail_public_key_t public_key;
|
||||
/* Position data flattened from PositionLite */
|
||||
int32_t latitude_i;
|
||||
int32_t longitude_i;
|
||||
int32_t altitude;
|
||||
uint32_t position_time;
|
||||
meshtastic_Position_LocSource position_source;
|
||||
/* Radio performance metrics */
|
||||
float snr;
|
||||
/* Last packet timestamp */
|
||||
uint32_t last_heard;
|
||||
/* Mesh routing metadata */
|
||||
uint8_t channel;
|
||||
uint8_t hops_away;
|
||||
uint8_t next_hop;
|
||||
/* Device metrics cached using integer scaling */
|
||||
uint8_t battery_level;
|
||||
uint32_t uptime_seconds;
|
||||
uint16_t channel_utilization_permille;
|
||||
uint16_t air_util_tx_permille;
|
||||
uint16_t voltage_millivolts;
|
||||
/* Bitset storing boolean flags and presence markers.
|
||||
See NodeDetailFlag shifts for decoded meaning. */
|
||||
uint32_t flags;
|
||||
} meshtastic_NodeDetail;
|
||||
|
||||
/* This message is never sent over the wire, but it is used for serializing DB
|
||||
state to flash in the device code
|
||||
FIXME, since we write this each time we enter deep sleep (and have infinite
|
||||
@@ -148,7 +191,7 @@ typedef struct _meshtastic_NodeDatabase {
|
||||
NodeDB.cpp in the device code. */
|
||||
uint32_t version;
|
||||
/* New lite version of NodeDB to decrease memory footprint */
|
||||
std::vector<meshtastic_NodeInfoLite> nodes;
|
||||
std::vector<meshtastic_NodeDetail> nodes;
|
||||
} meshtastic_NodeDatabase;
|
||||
|
||||
/* The on-disk saved channels */
|
||||
@@ -191,6 +234,7 @@ extern "C" {
|
||||
#define meshtastic_PositionLite_init_default {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
|
||||
#define meshtastic_UserLite_init_default {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0}
|
||||
#define meshtastic_NodeInfoLite_init_default {0, false, meshtastic_UserLite_init_default, false, meshtastic_PositionLite_init_default, 0, 0, false, meshtastic_DeviceMetrics_init_default, 0, 0, false, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_NodeDetail_init_default {0, {0}, "", "", _meshtastic_HardwareModel_MIN, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, 0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_DeviceState_init_default {false, meshtastic_MyNodeInfo_init_default, false, meshtastic_User_init_default, 0, {meshtastic_MeshPacket_init_default}, false, meshtastic_MeshPacket_init_default, 0, 0, 0, false, meshtastic_MeshPacket_init_default, 0, {meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default, meshtastic_NodeRemoteHardwarePin_init_default}}
|
||||
#define meshtastic_NodeDatabase_init_default {0, {0}}
|
||||
#define meshtastic_ChannelFile_init_default {0, {meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default, meshtastic_Channel_init_default}, 0}
|
||||
@@ -198,6 +242,7 @@ extern "C" {
|
||||
#define meshtastic_PositionLite_init_zero {0, 0, 0, 0, _meshtastic_Position_LocSource_MIN}
|
||||
#define meshtastic_UserLite_init_zero {{0}, "", "", _meshtastic_HardwareModel_MIN, 0, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, false, 0}
|
||||
#define meshtastic_NodeInfoLite_init_zero {0, false, meshtastic_UserLite_init_zero, false, meshtastic_PositionLite_init_zero, 0, 0, false, meshtastic_DeviceMetrics_init_zero, 0, 0, false, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_NodeDetail_init_zero {0, {0}, "", "", _meshtastic_HardwareModel_MIN, _meshtastic_Config_DeviceConfig_Role_MIN, {0, {0}}, 0, 0, 0, 0, _meshtastic_Position_LocSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_DeviceState_init_zero {false, meshtastic_MyNodeInfo_init_zero, false, meshtastic_User_init_zero, 0, {meshtastic_MeshPacket_init_zero}, false, meshtastic_MeshPacket_init_zero, 0, 0, 0, false, meshtastic_MeshPacket_init_zero, 0, {meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero, meshtastic_NodeRemoteHardwarePin_init_zero}}
|
||||
#define meshtastic_NodeDatabase_init_zero {0, {0}}
|
||||
#define meshtastic_ChannelFile_init_zero {0, {meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero, meshtastic_Channel_init_zero}, 0}
|
||||
@@ -230,6 +275,29 @@ extern "C" {
|
||||
#define meshtastic_NodeInfoLite_is_ignored_tag 11
|
||||
#define meshtastic_NodeInfoLite_next_hop_tag 12
|
||||
#define meshtastic_NodeInfoLite_bitfield_tag 13
|
||||
#define meshtastic_NodeDetail_num_tag 1
|
||||
#define meshtastic_NodeDetail_macaddr_tag 2
|
||||
#define meshtastic_NodeDetail_long_name_tag 3
|
||||
#define meshtastic_NodeDetail_short_name_tag 4
|
||||
#define meshtastic_NodeDetail_hw_model_tag 5
|
||||
#define meshtastic_NodeDetail_role_tag 6
|
||||
#define meshtastic_NodeDetail_public_key_tag 7
|
||||
#define meshtastic_NodeDetail_latitude_i_tag 8
|
||||
#define meshtastic_NodeDetail_longitude_i_tag 9
|
||||
#define meshtastic_NodeDetail_altitude_tag 10
|
||||
#define meshtastic_NodeDetail_position_time_tag 11
|
||||
#define meshtastic_NodeDetail_position_source_tag 12
|
||||
#define meshtastic_NodeDetail_snr_tag 13
|
||||
#define meshtastic_NodeDetail_last_heard_tag 14
|
||||
#define meshtastic_NodeDetail_channel_tag 15
|
||||
#define meshtastic_NodeDetail_hops_away_tag 16
|
||||
#define meshtastic_NodeDetail_next_hop_tag 17
|
||||
#define meshtastic_NodeDetail_battery_level_tag 18
|
||||
#define meshtastic_NodeDetail_uptime_seconds_tag 19
|
||||
#define meshtastic_NodeDetail_channel_utilization_permille_tag 20
|
||||
#define meshtastic_NodeDetail_air_util_tx_permille_tag 21
|
||||
#define meshtastic_NodeDetail_voltage_millivolts_tag 22
|
||||
#define meshtastic_NodeDetail_flags_tag 23
|
||||
#define meshtastic_DeviceState_my_node_tag 2
|
||||
#define meshtastic_DeviceState_owner_tag 3
|
||||
#define meshtastic_DeviceState_receive_queue_tag 5
|
||||
@@ -292,6 +360,33 @@ X(a, STATIC, SINGULAR, UINT32, bitfield, 13)
|
||||
#define meshtastic_NodeInfoLite_position_MSGTYPE meshtastic_PositionLite
|
||||
#define meshtastic_NodeInfoLite_device_metrics_MSGTYPE meshtastic_DeviceMetrics
|
||||
|
||||
#define meshtastic_NodeDetail_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, num, 1) \
|
||||
X(a, STATIC, SINGULAR, FIXED_LENGTH_BYTES, macaddr, 2) \
|
||||
X(a, STATIC, SINGULAR, STRING, long_name, 3) \
|
||||
X(a, STATIC, SINGULAR, STRING, short_name, 4) \
|
||||
X(a, STATIC, SINGULAR, UENUM, hw_model, 5) \
|
||||
X(a, STATIC, SINGULAR, UENUM, role, 6) \
|
||||
X(a, STATIC, SINGULAR, BYTES, public_key, 7) \
|
||||
X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 8) \
|
||||
X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 9) \
|
||||
X(a, STATIC, SINGULAR, INT32, altitude, 10) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, position_time, 11) \
|
||||
X(a, STATIC, SINGULAR, UENUM, position_source, 12) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, snr, 13) \
|
||||
X(a, STATIC, SINGULAR, FIXED32, last_heard, 14) \
|
||||
X(a, STATIC, SINGULAR, UINT32, channel, 15) \
|
||||
X(a, STATIC, SINGULAR, UINT32, hops_away, 16) \
|
||||
X(a, STATIC, SINGULAR, UINT32, next_hop, 17) \
|
||||
X(a, STATIC, SINGULAR, UINT32, battery_level, 18) \
|
||||
X(a, STATIC, SINGULAR, UINT32, uptime_seconds, 19) \
|
||||
X(a, STATIC, SINGULAR, UINT32, channel_utilization_permille, 20) \
|
||||
X(a, STATIC, SINGULAR, UINT32, air_util_tx_permille, 21) \
|
||||
X(a, STATIC, SINGULAR, UINT32, voltage_millivolts, 22) \
|
||||
X(a, STATIC, SINGULAR, UINT32, flags, 23)
|
||||
#define meshtastic_NodeDetail_CALLBACK NULL
|
||||
#define meshtastic_NodeDetail_DEFAULT NULL
|
||||
|
||||
#define meshtastic_DeviceState_FIELDLIST(X, a) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, my_node, 2) \
|
||||
X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
|
||||
@@ -317,7 +412,7 @@ X(a, CALLBACK, REPEATED, MESSAGE, nodes, 2)
|
||||
extern bool meshtastic_NodeDatabase_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field);
|
||||
#define meshtastic_NodeDatabase_CALLBACK meshtastic_NodeDatabase_callback
|
||||
#define meshtastic_NodeDatabase_DEFAULT NULL
|
||||
#define meshtastic_NodeDatabase_nodes_MSGTYPE meshtastic_NodeInfoLite
|
||||
#define meshtastic_NodeDatabase_nodes_MSGTYPE meshtastic_NodeDetail
|
||||
|
||||
#define meshtastic_ChannelFile_FIELDLIST(X, a) \
|
||||
X(a, STATIC, REPEATED, MESSAGE, channels, 1) \
|
||||
@@ -343,6 +438,7 @@ X(a, STATIC, OPTIONAL, MESSAGE, owner, 6)
|
||||
extern const pb_msgdesc_t meshtastic_PositionLite_msg;
|
||||
extern const pb_msgdesc_t meshtastic_UserLite_msg;
|
||||
extern const pb_msgdesc_t meshtastic_NodeInfoLite_msg;
|
||||
extern const pb_msgdesc_t meshtastic_NodeDetail_msg;
|
||||
extern const pb_msgdesc_t meshtastic_DeviceState_msg;
|
||||
extern const pb_msgdesc_t meshtastic_NodeDatabase_msg;
|
||||
extern const pb_msgdesc_t meshtastic_ChannelFile_msg;
|
||||
@@ -352,6 +448,7 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg;
|
||||
#define meshtastic_PositionLite_fields &meshtastic_PositionLite_msg
|
||||
#define meshtastic_UserLite_fields &meshtastic_UserLite_msg
|
||||
#define meshtastic_NodeInfoLite_fields &meshtastic_NodeInfoLite_msg
|
||||
#define meshtastic_NodeDetail_fields &meshtastic_NodeDetail_msg
|
||||
#define meshtastic_DeviceState_fields &meshtastic_DeviceState_msg
|
||||
#define meshtastic_NodeDatabase_fields &meshtastic_NodeDatabase_msg
|
||||
#define meshtastic_ChannelFile_fields &meshtastic_ChannelFile_msg
|
||||
@@ -363,6 +460,7 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg;
|
||||
#define meshtastic_BackupPreferences_size 2277
|
||||
#define meshtastic_ChannelFile_size 718
|
||||
#define meshtastic_DeviceState_size 1737
|
||||
#define meshtastic_NodeDetail_size 182
|
||||
#define meshtastic_NodeInfoLite_size 196
|
||||
#define meshtastic_PositionLite_size 28
|
||||
#define meshtastic_UserLite_size 98
|
||||
|
||||
@@ -738,9 +738,9 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
|
||||
JSONArray nodesArray;
|
||||
|
||||
uint32_t readIndex = 0;
|
||||
const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
|
||||
const meshtastic_NodeDetail *tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
|
||||
while (tempNodeInfo != NULL) {
|
||||
if (tempNodeInfo->has_user) {
|
||||
if (detailHasFlag(*tempNodeInfo, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
JSONObject node;
|
||||
|
||||
char id[16];
|
||||
@@ -748,26 +748,25 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
|
||||
|
||||
node["id"] = new JSONValue(id);
|
||||
node["snr"] = new JSONValue(tempNodeInfo->snr);
|
||||
node["via_mqtt"] = new JSONValue(BoolToString(tempNodeInfo->via_mqtt));
|
||||
node["via_mqtt"] = new JSONValue(BoolToString(detailViaMqtt(*tempNodeInfo)));
|
||||
node["last_heard"] = new JSONValue((int)tempNodeInfo->last_heard);
|
||||
node["position"] = new JSONValue();
|
||||
|
||||
if (nodeDB->hasValidPosition(tempNodeInfo)) {
|
||||
JSONObject position;
|
||||
position["latitude"] = new JSONValue((float)tempNodeInfo->position.latitude_i * 1e-7);
|
||||
position["longitude"] = new JSONValue((float)tempNodeInfo->position.longitude_i * 1e-7);
|
||||
position["altitude"] = new JSONValue((int)tempNodeInfo->position.altitude);
|
||||
position["latitude"] = new JSONValue(static_cast<float>(tempNodeInfo->latitude_i) * 1e-7f);
|
||||
position["longitude"] = new JSONValue(static_cast<float>(tempNodeInfo->longitude_i) * 1e-7f);
|
||||
position["altitude"] = new JSONValue(static_cast<int>(tempNodeInfo->altitude));
|
||||
node["position"] = new JSONValue(position);
|
||||
}
|
||||
|
||||
node["long_name"] = new JSONValue(tempNodeInfo->user.long_name);
|
||||
node["short_name"] = new JSONValue(tempNodeInfo->user.short_name);
|
||||
node["long_name"] = new JSONValue(tempNodeInfo->long_name);
|
||||
node["short_name"] = new JSONValue(tempNodeInfo->short_name);
|
||||
char macStr[18];
|
||||
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X", tempNodeInfo->user.macaddr[0],
|
||||
tempNodeInfo->user.macaddr[1], tempNodeInfo->user.macaddr[2], tempNodeInfo->user.macaddr[3],
|
||||
tempNodeInfo->user.macaddr[4], tempNodeInfo->user.macaddr[5]);
|
||||
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X", tempNodeInfo->macaddr[0], tempNodeInfo->macaddr[1],
|
||||
tempNodeInfo->macaddr[2], tempNodeInfo->macaddr[3], tempNodeInfo->macaddr[4], tempNodeInfo->macaddr[5]);
|
||||
node["mac_address"] = new JSONValue(macStr);
|
||||
node["hw_model"] = new JSONValue(tempNodeInfo->user.hw_model);
|
||||
node["hw_model"] = new JSONValue(tempNodeInfo->hw_model);
|
||||
|
||||
nodesArray.push_back(new JSONValue(node));
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
/// Verify baseline assumption of node size. If it increases, we need to reevaluate
|
||||
/// the impact of its memory footprint, notably on MAX_NUM_NODES.
|
||||
static_assert(sizeof(meshtastic_NodeInfoLite) <= 200, "NodeInfoLite size increased. Reconsider impact on MAX_NUM_NODES.");
|
||||
static_assert(sizeof(meshtastic_NodeDetail) <= 200, "NodeDetail size increased. Reconsider impact on MAX_NUM_NODES.");
|
||||
|
||||
/// max number of nodes allowed in the nodeDB
|
||||
#ifndef MAX_NUM_NODES
|
||||
|
||||
@@ -107,14 +107,14 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
|
||||
// Automatically favorite the node that is using the admin key
|
||||
auto remoteNode = nodeDB->getMeshNode(mp.from);
|
||||
if (remoteNode && !remoteNode->is_favorite) {
|
||||
if (remoteNode && !detailIsFavorite(*remoteNode)) {
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_CLIENT_BASE) {
|
||||
// Special case for CLIENT_BASE: is_favorite has special meaning, and we don't want to automatically set it
|
||||
// without the user doing so deliberately.
|
||||
LOG_INFO("PKC admin valid, but not auto-favoriting node %x because role==CLIENT_BASE", mp.from);
|
||||
} else {
|
||||
LOG_INFO("PKC admin valid. Auto-favoriting node %x", mp.from);
|
||||
remoteNode->is_favorite = true;
|
||||
nodeDB->set_favorite(true, mp.from);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -341,9 +341,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
}
|
||||
case meshtastic_AdminMessage_set_favorite_node_tag: {
|
||||
LOG_INFO("Client received set_favorite_node command");
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_favorite_node);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(r->set_favorite_node);
|
||||
if (node != NULL) {
|
||||
node->is_favorite = true;
|
||||
detailSetFlag(*node, NODEDETAIL_FLAG_IS_FAVORITE, true);
|
||||
saveChanges(SEGMENT_NODEDATABASE, false);
|
||||
if (screen)
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); // <-- Rebuild screens
|
||||
@@ -352,9 +352,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
}
|
||||
case meshtastic_AdminMessage_remove_favorite_node_tag: {
|
||||
LOG_INFO("Client received remove_favorite_node command");
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_favorite_node);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(r->remove_favorite_node);
|
||||
if (node != NULL) {
|
||||
node->is_favorite = false;
|
||||
detailSetFlag(*node, NODEDETAIL_FLAG_IS_FAVORITE, false);
|
||||
saveChanges(SEGMENT_NODEDATABASE, false);
|
||||
if (screen)
|
||||
screen->setFrames(graphics::Screen::FOCUS_PRESERVE); // <-- Rebuild screens
|
||||
@@ -363,31 +363,32 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
}
|
||||
case meshtastic_AdminMessage_set_ignored_node_tag: {
|
||||
LOG_INFO("Client received set_ignored_node command");
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->set_ignored_node);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(r->set_ignored_node);
|
||||
if (node != NULL) {
|
||||
node->is_ignored = true;
|
||||
node->has_device_metrics = false;
|
||||
node->has_position = false;
|
||||
node->user.public_key.size = 0;
|
||||
node->user.public_key.bytes[0] = 0;
|
||||
detailSetFlag(*node, NODEDETAIL_FLAG_IS_IGNORED, true);
|
||||
clearMetricsFromDetail(*node);
|
||||
clearPositionFromDetail(*node);
|
||||
node->public_key.size = 0;
|
||||
memset(node->public_key.bytes, 0, sizeof(node->public_key.bytes));
|
||||
saveChanges(SEGMENT_NODEDATABASE, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_AdminMessage_remove_ignored_node_tag: {
|
||||
LOG_INFO("Client received remove_ignored_node command");
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(r->remove_ignored_node);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(r->remove_ignored_node);
|
||||
if (node != NULL) {
|
||||
node->is_ignored = false;
|
||||
detailSetFlag(*node, NODEDETAIL_FLAG_IS_IGNORED, false);
|
||||
saveChanges(SEGMENT_NODEDATABASE, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case meshtastic_AdminMessage_set_fixed_position_tag: {
|
||||
LOG_INFO("Client received set_fixed_position command");
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
node->has_position = true;
|
||||
node->position = TypeConversions::ConvertToPositionLite(r->set_fixed_position);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
if (node) {
|
||||
applyPositionToDetail(*node, r->set_fixed_position);
|
||||
}
|
||||
nodeDB->setLocalPosition(r->set_fixed_position);
|
||||
config.position.fixed_position = true;
|
||||
saveChanges(SEGMENT_NODEDATABASE | SEGMENT_CONFIG, false);
|
||||
|
||||
@@ -131,9 +131,9 @@ void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t
|
||||
}
|
||||
|
||||
static bool returnToCannedList = false;
|
||||
bool hasKeyForNode(const meshtastic_NodeInfoLite *node)
|
||||
bool hasKeyForNode(const meshtastic_NodeDetail *node)
|
||||
{
|
||||
return node && node->has_user && node->user.public_key.size > 0;
|
||||
return node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER) && node->public_key.size > 0;
|
||||
}
|
||||
/**
|
||||
* @brief Items in array this->messages will be set to be pointing on the right
|
||||
@@ -254,11 +254,11 @@ void CannedMessageModule::updateDestinationSelectionList()
|
||||
this->filteredNodes.reserve(numMeshNodes);
|
||||
|
||||
for (size_t i = 0; i < numMeshNodes; ++i) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
|
||||
if (!node || node->num == myNodeNum || !node->has_user || node->user.public_key.size != 32)
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNodeByIndex(i);
|
||||
if (!node || node->num == myNodeNum || !detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER) || node->public_key.size != 32)
|
||||
continue;
|
||||
|
||||
const String &nodeName = node->user.long_name;
|
||||
const String nodeName = node->long_name;
|
||||
|
||||
if (searchQuery.length() == 0) {
|
||||
this->filteredNodes.push_back({node, sinceLastSeen(node)});
|
||||
@@ -525,7 +525,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
|
||||
} else {
|
||||
int nodeIndex = destIndex - static_cast<int>(activeChannelIndices.size());
|
||||
if (nodeIndex >= 0 && nodeIndex < static_cast<int>(filteredNodes.size())) {
|
||||
const meshtastic_NodeInfoLite *selectedNode = filteredNodes[nodeIndex].node;
|
||||
const meshtastic_NodeDetail *selectedNode = filteredNodes[nodeIndex].node;
|
||||
if (selectedNode) {
|
||||
dest = selectedNode->num;
|
||||
channel = selectedNode->channel;
|
||||
@@ -1253,9 +1253,9 @@ const char *CannedMessageModule::getNodeName(NodeNum node)
|
||||
if (node == NODENUM_BROADCAST)
|
||||
return "Broadcast";
|
||||
|
||||
meshtastic_NodeInfoLite *info = nodeDB->getMeshNode(node);
|
||||
if (info && info->has_user && strlen(info->user.long_name) > 0) {
|
||||
return info->user.long_name;
|
||||
meshtastic_NodeDetail *info = nodeDB->getMeshNode(node);
|
||||
if (info && detailHasFlag(*info, NODEDETAIL_FLAG_HAS_USER) && strlen(info->long_name) > 0) {
|
||||
return info->long_name;
|
||||
}
|
||||
|
||||
static char fallback[12];
|
||||
@@ -1565,18 +1565,18 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O
|
||||
else {
|
||||
int nodeIndex = itemIndex - numActiveChannels;
|
||||
if (nodeIndex >= 0 && nodeIndex < static_cast<int>(this->filteredNodes.size())) {
|
||||
meshtastic_NodeInfoLite *node = this->filteredNodes[nodeIndex].node;
|
||||
meshtastic_NodeDetail *node = this->filteredNodes[nodeIndex].node;
|
||||
if (node) {
|
||||
if (node->is_favorite) {
|
||||
if (detailIsFavorite(*node)) {
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
snprintf(entryText, sizeof(entryText), "* %s", node->user.short_name);
|
||||
snprintf(entryText, sizeof(entryText), "* %s", node->short_name);
|
||||
} else {
|
||||
snprintf(entryText, sizeof(entryText), "%s", node->user.short_name);
|
||||
snprintf(entryText, sizeof(entryText), "%s", node->short_name);
|
||||
}
|
||||
#else
|
||||
snprintf(entryText, sizeof(entryText), "* %s", node->user.long_name);
|
||||
snprintf(entryText, sizeof(entryText), "* %s", node->long_name);
|
||||
} else {
|
||||
snprintf(entryText, sizeof(entryText), "%s", node->user.long_name);
|
||||
snprintf(entryText, sizeof(entryText), "%s", node->long_name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1601,7 +1601,7 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O
|
||||
if (itemIndex >= numActiveChannels) {
|
||||
int nodeIndex = itemIndex - numActiveChannels;
|
||||
if (nodeIndex >= 0 && nodeIndex < static_cast<int>(this->filteredNodes.size())) {
|
||||
const meshtastic_NodeInfoLite *node = this->filteredNodes[nodeIndex].node;
|
||||
const meshtastic_NodeDetail *node = this->filteredNodes[nodeIndex].node;
|
||||
if (node && hasKeyForNode(node)) {
|
||||
int iconX = display->getWidth() - key_symbol_width - 15;
|
||||
int iconY = yOffset + (FONT_HEIGHT_SMALL - key_symbol_height) / 2;
|
||||
|
||||
@@ -45,7 +45,7 @@ struct Letter {
|
||||
};
|
||||
|
||||
struct NodeEntry {
|
||||
meshtastic_NodeInfoLite *node;
|
||||
meshtastic_NodeDetail *node;
|
||||
uint32_t lastHeard;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#if !MESHTASTIC_EXCLUDE_PKI
|
||||
#include "KeyVerificationModule.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "graphics/draw/MenuHandler.h"
|
||||
#include "main.h"
|
||||
@@ -36,8 +37,9 @@ AdminMessageHandleResult KeyVerificationModule::handleAdminMessageForModule(cons
|
||||
|
||||
} else if (request->key_verification.message_type == meshtastic_KeyVerificationAdmin_MessageType_DO_VERIFY &&
|
||||
request->key_verification.nonce == currentNonce) {
|
||||
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
|
||||
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
if (auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode)) {
|
||||
detailSetFlag(*remoteNodePtr, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED);
|
||||
}
|
||||
resetToIdle();
|
||||
} else if (request->key_verification.message_type == meshtastic_KeyVerificationAdmin_MessageType_DO_NOT_VERIFY) {
|
||||
resetToIdle();
|
||||
@@ -72,8 +74,9 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
|
||||
sprintf(cn->message, "Enter Security Number for Key Verification");
|
||||
cn->which_payload_variant = meshtastic_ClientNotification_key_verification_number_request_tag;
|
||||
cn->payload_variant.key_verification_number_request.nonce = currentNonce;
|
||||
strncpy(cn->payload_variant.key_verification_number_request.remote_longname, // should really check for nulls, etc
|
||||
nodeDB->getMeshNode(currentRemoteNode)->user.long_name,
|
||||
const meshtastic_NodeDetail *remoteNode = nodeDB->getMeshNode(currentRemoteNode);
|
||||
const char *remoteLongName = (remoteNode && remoteNode->long_name[0] != '\0') ? remoteNode->long_name : "";
|
||||
strncpy(cn->payload_variant.key_verification_number_request.remote_longname, remoteLongName,
|
||||
sizeof(cn->payload_variant.key_verification_number_request.remote_longname));
|
||||
service->sendClientNotification(cn);
|
||||
LOG_INFO("Received hash2");
|
||||
@@ -94,8 +97,9 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
|
||||
options.bannerCallback =
|
||||
[=](int selected) {
|
||||
if (selected == 1) {
|
||||
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
|
||||
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
if (auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode)) {
|
||||
detailSetFlag(*remoteNodePtr, NODEDETAIL_FLAG_IS_KEY_MANUALLY_VERIFIED);
|
||||
}
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(options);)
|
||||
@@ -104,8 +108,10 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
|
||||
sprintf(cn->message, "Final confirmation for incoming manual key verification %s", message);
|
||||
cn->which_payload_variant = meshtastic_ClientNotification_key_verification_final_tag;
|
||||
cn->payload_variant.key_verification_final.nonce = currentNonce;
|
||||
strncpy(cn->payload_variant.key_verification_final.remote_longname, // should really check for nulls, etc
|
||||
nodeDB->getMeshNode(currentRemoteNode)->user.long_name,
|
||||
const meshtastic_NodeDetail *remoteNodeFinal = nodeDB->getMeshNode(currentRemoteNode);
|
||||
const char *remoteFinalLongName =
|
||||
(remoteNodeFinal && remoteNodeFinal->long_name[0] != '\0') ? remoteNodeFinal->long_name : "";
|
||||
strncpy(cn->payload_variant.key_verification_final.remote_longname, remoteFinalLongName,
|
||||
sizeof(cn->payload_variant.key_verification_final.remote_longname));
|
||||
cn->payload_variant.key_verification_final.isSender = false;
|
||||
service->sendClientNotification(cn);
|
||||
@@ -202,8 +208,9 @@ meshtastic_MeshPacket *KeyVerificationModule::allocReply()
|
||||
currentSecurityNumber % 1000);
|
||||
cn->which_payload_variant = meshtastic_ClientNotification_key_verification_number_inform_tag;
|
||||
cn->payload_variant.key_verification_number_inform.nonce = currentNonce;
|
||||
strncpy(cn->payload_variant.key_verification_number_inform.remote_longname, // should really check for nulls, etc
|
||||
nodeDB->getMeshNode(currentRemoteNode)->user.long_name,
|
||||
meshtastic_NodeDetail *remoteNode = nodeDB->getMeshNode(currentRemoteNode);
|
||||
const char *remoteName = (remoteNode && detailHasFlag(*remoteNode, NODEDETAIL_FLAG_HAS_USER)) ? remoteNode->long_name : "";
|
||||
strncpy(cn->payload_variant.key_verification_number_inform.remote_longname, remoteName,
|
||||
sizeof(cn->payload_variant.key_verification_number_inform.remote_longname));
|
||||
cn->payload_variant.key_verification_number_inform.security_number = currentSecurityNumber;
|
||||
service->sendClientNotification(cn);
|
||||
@@ -217,9 +224,9 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
|
||||
NodeNum ourNodeNum = nodeDB->getNodeNum();
|
||||
uint8_t scratch_hash[32] = {0};
|
||||
LOG_WARN("received security number: %u", incomingNumber);
|
||||
meshtastic_NodeInfoLite *remoteNodePtr = nullptr;
|
||||
remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
|
||||
if (remoteNodePtr == nullptr || !remoteNodePtr->has_user || remoteNodePtr->user.public_key.size != 32) {
|
||||
meshtastic_NodeDetail *remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
|
||||
if (remoteNodePtr == nullptr || !detailHasFlag(*remoteNodePtr, NODEDETAIL_FLAG_HAS_USER) ||
|
||||
remoteNodePtr->public_key.size != 32) {
|
||||
currentState = KEY_VERIFICATION_IDLE;
|
||||
return; // should we throw an error here?
|
||||
}
|
||||
@@ -232,7 +239,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
|
||||
hash.update(¤tRemoteNode, sizeof(currentRemoteNode));
|
||||
hash.update(owner.public_key.bytes, owner.public_key.size);
|
||||
|
||||
hash.update(remoteNodePtr->user.public_key.bytes, remoteNodePtr->user.public_key.size);
|
||||
hash.update(remoteNodePtr->public_key.bytes, remoteNodePtr->public_key.size);
|
||||
hash.finalize(hash1, 32);
|
||||
|
||||
hash.reset();
|
||||
@@ -265,8 +272,10 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
|
||||
sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message);
|
||||
cn->which_payload_variant = meshtastic_ClientNotification_key_verification_final_tag;
|
||||
cn->payload_variant.key_verification_final.nonce = currentNonce;
|
||||
strncpy(cn->payload_variant.key_verification_final.remote_longname, // should really check for nulls, etc
|
||||
nodeDB->getMeshNode(currentRemoteNode)->user.long_name,
|
||||
meshtastic_NodeDetail *remoteNodeFinal = nodeDB->getMeshNode(currentRemoteNode);
|
||||
const char *finalRemoteName =
|
||||
(remoteNodeFinal && detailHasFlag(*remoteNodeFinal, NODEDETAIL_FLAG_HAS_USER)) ? remoteNodeFinal->long_name : "";
|
||||
strncpy(cn->payload_variant.key_verification_final.remote_longname, finalRemoteName,
|
||||
sizeof(cn->payload_variant.key_verification_final.remote_longname));
|
||||
cn->payload_variant.key_verification_final.isSender = true;
|
||||
service->sendClientNotification(cn);
|
||||
|
||||
@@ -169,8 +169,9 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *node = service->refreshLocalMeshNode(); // should guarantee there is now a position
|
||||
assert(node->has_position);
|
||||
meshtastic_NodeDetail *node = service->refreshLocalMeshNode(); // should guarantee there is now a position
|
||||
assert(detailHasFlag(*node, NODEDETAIL_FLAG_HAS_POSITION));
|
||||
meshtastic_PositionLite nodePosition = detailToPositionLite(*node);
|
||||
|
||||
// configuration of POSITION packet
|
||||
// consider making this a function argument?
|
||||
@@ -180,7 +181,7 @@ meshtastic_MeshPacket *PositionModule::allocPositionPacket()
|
||||
meshtastic_Position p = meshtastic_Position_init_default; // Start with an empty structure
|
||||
// if localPosition is totally empty, put our last saved position (lite) in there
|
||||
if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) {
|
||||
nodeDB->setLocalPosition(TypeConversions::ConvertToPosition(node->position));
|
||||
nodeDB->setLocalPosition(TypeConversions::ConvertToPosition(nodePosition));
|
||||
}
|
||||
localPosition.seq_number++;
|
||||
|
||||
@@ -401,7 +402,7 @@ int32_t PositionModule::runOnce()
|
||||
doDeepSleep(nightyNightMs, false, false);
|
||||
}
|
||||
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
if (node == nullptr)
|
||||
return RUNONCE_INTERVAL;
|
||||
|
||||
@@ -420,8 +421,9 @@ int32_t PositionModule::runOnce()
|
||||
if (nodeDB->hasValidPosition(node)) {
|
||||
lastGpsSend = now;
|
||||
|
||||
lastGpsLatitude = node->position.latitude_i;
|
||||
lastGpsLongitude = node->position.longitude_i;
|
||||
meshtastic_PositionLite nodePosition = detailToPositionLite(*node);
|
||||
lastGpsLatitude = nodePosition.latitude_i;
|
||||
lastGpsLongitude = nodePosition.longitude_i;
|
||||
|
||||
sendOurPosition();
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_LOST_AND_FOUND) {
|
||||
@@ -429,12 +431,11 @@ int32_t PositionModule::runOnce()
|
||||
}
|
||||
}
|
||||
} else if (config.position.position_broadcast_smart_enabled) {
|
||||
const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position
|
||||
const meshtastic_NodeDetail *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position
|
||||
|
||||
if (nodeDB->hasValidPosition(node2)) {
|
||||
// The minimum time (in seconds) that would pass before we are able to send a new position packet.
|
||||
|
||||
auto smartPosition = getDistanceTraveledSinceLastSend(node->position);
|
||||
auto smartPosition = getDistanceTraveledSinceLastSend(detailToPositionLite(*node));
|
||||
msSinceLastSend = now - lastGpsSend;
|
||||
|
||||
if (smartPosition.hasTraveledOverThreshold &&
|
||||
@@ -448,8 +449,9 @@ int32_t PositionModule::runOnce()
|
||||
msSinceLastSend, minimumTimeThreshold);
|
||||
|
||||
// Set the current coords as our last ones, after we've compared distance with current and decided to send
|
||||
lastGpsLatitude = node->position.latitude_i;
|
||||
lastGpsLongitude = node->position.longitude_i;
|
||||
meshtastic_PositionLite nodePosition = detailToPositionLite(*node);
|
||||
lastGpsLatitude = nodePosition.latitude_i;
|
||||
lastGpsLongitude = nodePosition.longitude_i;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -489,11 +491,11 @@ struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic
|
||||
|
||||
void PositionModule::handleNewPosition()
|
||||
{
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
const meshtastic_NodeInfoLite *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
const meshtastic_NodeDetail *node2 = service->refreshLocalMeshNode(); // should guarantee there is now a position
|
||||
// We limit our GPS broadcasts to a max rate
|
||||
if (nodeDB->hasValidPosition(node2)) {
|
||||
auto smartPosition = getDistanceTraveledSinceLastSend(node->position);
|
||||
auto smartPosition = getDistanceTraveledSinceLastSend(detailToPositionLite(*node));
|
||||
uint32_t msSinceLastSend = millis() - lastGpsSend;
|
||||
if (smartPosition.hasTraveledOverThreshold &&
|
||||
Throttle::execute(
|
||||
@@ -505,8 +507,9 @@ void PositionModule::handleNewPosition()
|
||||
minimumTimeThreshold);
|
||||
|
||||
// Set the current coords as our last ones, after we've compared distance with current and decided to send
|
||||
lastGpsLatitude = node->position.latitude_i;
|
||||
lastGpsLongitude = node->position.longitude_i;
|
||||
meshtastic_PositionLite nodePosition = detailToPositionLite(*node);
|
||||
lastGpsLatitude = nodePosition.latitude_i;
|
||||
lastGpsLongitude = nodePosition.longitude_i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ ProcessMessage RangeTestModuleRadio::handleReceived(const meshtastic_MeshPacket
|
||||
}
|
||||
|
||||
/*
|
||||
NodeInfoLite *n = nodeDB->getMeshNode(getFrom(&mp));
|
||||
meshtastic_NodeDetail *n = nodeDB->getMeshNode(getFrom(&mp));
|
||||
|
||||
LOG_DEBUG("-----------------------------------------");
|
||||
LOG_DEBUG("p.payload.bytes \"%s\"", p.payload.bytes);
|
||||
@@ -189,7 +189,7 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
|
||||
#ifdef ARCH_ESP32
|
||||
auto &p = mp.decoded;
|
||||
|
||||
meshtastic_NodeInfoLite *n = nodeDB->getMeshNode(getFrom(&mp));
|
||||
meshtastic_NodeDetail *n = nodeDB->getMeshNode(getFrom(&mp));
|
||||
/*
|
||||
LOG_DEBUG("-----------------------------------------");
|
||||
LOG_DEBUG("p.payload.bytes \"%s\"", p.payload.bytes);
|
||||
@@ -268,27 +268,33 @@ bool RangeTestModuleRadio::appendFile(const meshtastic_MeshPacket &mp)
|
||||
fileToAppend.printf("??:??:??,"); // Time
|
||||
}
|
||||
|
||||
fileToAppend.printf("%d,", getFrom(&mp)); // From
|
||||
fileToAppend.printf("%s,", n->user.long_name); // Long Name
|
||||
fileToAppend.printf("%f,", n->position.latitude_i * 1e-7); // Sender Lat
|
||||
fileToAppend.printf("%f,", n->position.longitude_i * 1e-7); // Sender Long
|
||||
fileToAppend.printf("%d,", getFrom(&mp)); // From
|
||||
const char *senderName = (n && strlen(n->long_name) > 0) ? n->long_name : "?";
|
||||
fileToAppend.printf("%s,", senderName); // Long Name
|
||||
double senderLat = (n && detailHasFlag(*n, NODEDETAIL_FLAG_HAS_POSITION)) ? n->latitude_i * 1e-7 : 0.0;
|
||||
double senderLon = (n && detailHasFlag(*n, NODEDETAIL_FLAG_HAS_POSITION)) ? n->longitude_i * 1e-7 : 0.0;
|
||||
fileToAppend.printf("%f,", senderLat); // Sender Lat
|
||||
fileToAppend.printf("%f,", senderLon); // Sender Long
|
||||
if (gpsStatus->getIsConnected() || config.position.fixed_position) {
|
||||
fileToAppend.printf("%f,", gpsStatus->getLatitude() * 1e-7); // RX Lat
|
||||
fileToAppend.printf("%f,", gpsStatus->getLongitude() * 1e-7); // RX Long
|
||||
fileToAppend.printf("%d,", gpsStatus->getAltitude()); // RX Altitude
|
||||
} else {
|
||||
// When the phone API is in use, the node info will be updated with position
|
||||
meshtastic_NodeInfoLite *us = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
fileToAppend.printf("%f,", us->position.latitude_i * 1e-7); // RX Lat
|
||||
fileToAppend.printf("%f,", us->position.longitude_i * 1e-7); // RX Long
|
||||
fileToAppend.printf("%d,", us->position.altitude); // RX Altitude
|
||||
meshtastic_NodeDetail *us = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
double rxLat = (us && detailHasFlag(*us, NODEDETAIL_FLAG_HAS_POSITION)) ? us->latitude_i * 1e-7 : 0.0;
|
||||
double rxLon = (us && detailHasFlag(*us, NODEDETAIL_FLAG_HAS_POSITION)) ? us->longitude_i * 1e-7 : 0.0;
|
||||
int32_t rxAlt = (us && detailHasFlag(*us, NODEDETAIL_FLAG_HAS_POSITION)) ? us->altitude : 0;
|
||||
fileToAppend.printf("%f,", rxLat); // RX Lat
|
||||
fileToAppend.printf("%f,", rxLon); // RX Long
|
||||
fileToAppend.printf("%d,", rxAlt); // RX Altitude
|
||||
}
|
||||
|
||||
fileToAppend.printf("%f,", mp.rx_snr); // RX SNR
|
||||
|
||||
if (n->position.latitude_i && n->position.longitude_i && gpsStatus->getLatitude() && gpsStatus->getLongitude()) {
|
||||
float distance = GeoCoord::latLongToMeter(n->position.latitude_i * 1e-7, n->position.longitude_i * 1e-7,
|
||||
gpsStatus->getLatitude() * 1e-7, gpsStatus->getLongitude() * 1e-7);
|
||||
if (n && detailHasFlag(*n, NODEDETAIL_FLAG_HAS_POSITION) && gpsStatus->getLatitude() && gpsStatus->getLongitude()) {
|
||||
float distance = GeoCoord::latLongToMeter(n->latitude_i * 1e-7, n->longitude_i * 1e-7, gpsStatus->getLatitude() * 1e-7,
|
||||
gpsStatus->getLongitude() * 1e-7);
|
||||
fileToAppend.printf("%f,", distance); // Distance in meters
|
||||
} else {
|
||||
fileToAppend.printf("0,");
|
||||
|
||||
@@ -17,8 +17,10 @@ bool RoutingModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mesh
|
||||
config.device.rebroadcast_mode == meshtastic_Config_DeviceConfig_RebroadcastMode_KNOWN_ONLY)) {
|
||||
if (!maybePKI)
|
||||
return false;
|
||||
if ((nodeDB->getMeshNode(mp.from) == NULL || !nodeDB->getMeshNode(mp.from)->has_user) &&
|
||||
(nodeDB->getMeshNode(mp.to) == NULL || !nodeDB->getMeshNode(mp.to)->has_user))
|
||||
const meshtastic_NodeDetail *fromDetail = nodeDB->getMeshNode(mp.from);
|
||||
const meshtastic_NodeDetail *toDetail = nodeDB->getMeshNode(mp.to);
|
||||
if ((!fromDetail || !detailHasFlag(*fromDetail, NODEDETAIL_FLAG_HAS_USER)) &&
|
||||
(!toDetail || !detailHasFlag(*toDetail, NODEDETAIL_FLAG_HAS_USER)))
|
||||
return false;
|
||||
} else if (owner.is_licensed && nodeDB->getLicenseStatus(mp.from) == UserLicenseStatus::NotLicensed) {
|
||||
// Don't let licensed users to rebroadcast packets from unlicensed users
|
||||
|
||||
@@ -84,6 +84,42 @@ SerialModule::SerialModule() : StreamAPI(&Serial2), concurrency::OSThread("Seria
|
||||
static Print *serialPrint = &Serial2;
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
const char *resolveNodeName(const meshtastic_NodeDetail *node, char *buffer, size_t bufferSize, bool preferLongName)
|
||||
{
|
||||
if (!node) {
|
||||
return "???";
|
||||
}
|
||||
|
||||
if (detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
if (preferLongName) {
|
||||
if (node->long_name[0]) {
|
||||
return node->long_name;
|
||||
}
|
||||
if (node->short_name[0]) {
|
||||
return node->short_name;
|
||||
}
|
||||
} else {
|
||||
if (node->short_name[0]) {
|
||||
return node->short_name;
|
||||
}
|
||||
if (node->long_name[0]) {
|
||||
return node->long_name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer && bufferSize > 0) {
|
||||
snprintf(buffer, bufferSize, "(%04X)", static_cast<unsigned int>(node->num & 0xFFFF));
|
||||
buffer[bufferSize - 1] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return "???";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
char serialBytes[512];
|
||||
size_t serialPayloadSize;
|
||||
|
||||
@@ -249,13 +285,15 @@ int32_t SerialModule::runOnce()
|
||||
if (!Throttle::isWithinTimespanMs(lastNmeaTime, 10000)) {
|
||||
lastNmeaTime = millis();
|
||||
uint32_t readIndex = 0;
|
||||
const meshtastic_NodeInfoLite *tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
|
||||
while (tempNodeInfo != NULL) {
|
||||
if (tempNodeInfo->has_user && nodeDB->hasValidPosition(tempNodeInfo)) {
|
||||
printWPL(outbuf, sizeof(outbuf), tempNodeInfo->position, tempNodeInfo->user.long_name, true);
|
||||
const meshtastic_NodeDetail *tempNode = nodeDB->readNextMeshNode(readIndex);
|
||||
while (tempNode != NULL) {
|
||||
if (detailHasFlag(*tempNode, NODEDETAIL_FLAG_HAS_USER) && nodeDB->hasValidPosition(tempNode)) {
|
||||
char nameBuffer[12] = {0};
|
||||
const char *name = resolveNodeName(tempNode, nameBuffer, sizeof(nameBuffer), true);
|
||||
printWPL(outbuf, sizeof(outbuf), detailToPositionLite(*tempNode), name, true);
|
||||
serialPrint->printf("%s", outbuf);
|
||||
}
|
||||
tempNodeInfo = nodeDB->readNextMeshNode(readIndex);
|
||||
tempNode = nodeDB->readNextMeshNode(readIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,8 +440,9 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp
|
||||
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_SIMPLE) {
|
||||
serialPrint->write(p.payload.bytes, p.payload.size);
|
||||
} else if (moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG) {
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
|
||||
const char *sender = (node && node->has_user) ? node->user.short_name : "???";
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(getFrom(&mp));
|
||||
char senderBuffer[12] = {0};
|
||||
const char *sender = resolveNodeName(node, senderBuffer, sizeof(senderBuffer), false);
|
||||
serialPrint->println();
|
||||
serialPrint->printf("%s: %s", sender, p.payload.bytes);
|
||||
serialPrint->println();
|
||||
@@ -419,7 +458,10 @@ ProcessMessage SerialModuleRadio::handleReceived(const meshtastic_MeshPacket &mp
|
||||
decoded = &scratch;
|
||||
}
|
||||
// send position packet as WPL to the serial port
|
||||
printWPL(outbuf, sizeof(outbuf), *decoded, nodeDB->getMeshNode(getFrom(&mp))->user.long_name,
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(getFrom(&mp));
|
||||
char nameBuffer[12] = {0};
|
||||
const char *name = resolveNodeName(node, nameBuffer, sizeof(nameBuffer), true);
|
||||
printWPL(outbuf, sizeof(outbuf), *decoded, name,
|
||||
moduleConfig.serial.mode == meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO);
|
||||
serialPrint->printf("%s", outbuf);
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ void TraceRouteModule::maybeSetNextHop(NodeNum target, uint8_t nextHopByte)
|
||||
if (target == NODENUM_BROADCAST)
|
||||
return;
|
||||
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(target);
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(target);
|
||||
if (node && node->next_hop != nextHopByte) {
|
||||
LOG_INFO("Updating next-hop for 0x%08x to 0x%02x based on traceroute", target, nextHopByte);
|
||||
node->next_hop = nextHopByte;
|
||||
@@ -490,13 +490,13 @@ TraceRouteModule::TraceRouteModule()
|
||||
|
||||
const char *TraceRouteModule::getNodeName(NodeNum node)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = nodeDB->getMeshNode(node);
|
||||
if (info && info->has_user) {
|
||||
if (strlen(info->user.short_name) > 0) {
|
||||
return info->user.short_name;
|
||||
const meshtastic_NodeDetail *info = nodeDB->getMeshNode(node);
|
||||
if (info && detailHasFlag(*info, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
if (strlen(info->short_name) > 0) {
|
||||
return info->short_name;
|
||||
}
|
||||
if (strlen(info->user.long_name) > 0) {
|
||||
return info->user.long_name;
|
||||
if (strlen(info->long_name) > 0) {
|
||||
return info->long_name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
static char distStr[20];
|
||||
|
||||
// Get our node, to use our own position
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
meshtastic_NodeDetail *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
|
||||
// Text fields to draw (left of compass)
|
||||
// Last element must be NULL. This signals the end of the char*[] to drawColumns
|
||||
@@ -135,7 +135,11 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
|
||||
// If our node has a position:
|
||||
if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading())) {
|
||||
const meshtastic_PositionLite &op = ourNode->position;
|
||||
meshtastic_PositionLite op = meshtastic_PositionLite_init_default;
|
||||
op.latitude_i = ourNode->latitude_i;
|
||||
op.longitude_i = ourNode->longitude_i;
|
||||
op.altitude = ourNode->altitude;
|
||||
op.location_source = ourNode->position_source;
|
||||
float myHeading;
|
||||
if (uiconfig.compass_mode == meshtastic_CompassMode_FREEZE_HEADING) {
|
||||
myHeading = 0;
|
||||
|
||||
@@ -131,11 +131,12 @@ inline void onReceiveProto(char *topic, byte *payload, size_t length)
|
||||
|
||||
// PKI messages get accepted even if we can't decrypt
|
||||
if (router && p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag && strcmp(e.channel_id, "PKI") == 0) {
|
||||
const meshtastic_NodeInfoLite *tx = nodeDB->getMeshNode(getFrom(p.get()));
|
||||
const meshtastic_NodeInfoLite *rx = nodeDB->getMeshNode(p->to);
|
||||
meshtastic_NodeDetail *tx = nodeDB->getMeshNode(getFrom(p.get()));
|
||||
meshtastic_NodeDetail *rx = nodeDB->getMeshNode(p->to);
|
||||
// Only accept PKI messages to us, or if we have both the sender and receiver in our nodeDB, as then it's
|
||||
// likely they discovered each other via a channel we have downlink enabled for
|
||||
if (isToUs(p.get()) || (tx && tx->has_user && rx && rx->has_user))
|
||||
if (isToUs(p.get()) ||
|
||||
(tx && detailHasFlag(*tx, NODEDETAIL_FLAG_HAS_USER) && rx && detailHasFlag(*rx, NODEDETAIL_FLAG_HAS_USER)))
|
||||
router->enqueueReceivedMessage(p.release());
|
||||
} else if (router &&
|
||||
perhapsDecode(p.get()) == DecodeState::DECODE_SUCCESS) // ignore messages if we don't have the channel key
|
||||
|
||||
@@ -313,10 +313,11 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
// Lambda function for adding a long name to the route
|
||||
auto addToRoute = [](JSONArray *route, NodeNum num) {
|
||||
char long_name[40] = "Unknown";
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
|
||||
bool name_known = node ? node->has_user : false;
|
||||
if (name_known)
|
||||
memcpy(long_name, node->user.long_name, sizeof(long_name));
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(num);
|
||||
if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
strncpy(long_name, node->long_name, sizeof(long_name) - 1);
|
||||
long_name[sizeof(long_name) - 1] = '\0';
|
||||
}
|
||||
route->push_back(new JSONValue(long_name));
|
||||
};
|
||||
addToRoute(&route, mp->to); // Started at the original transmitter (destination of response)
|
||||
|
||||
@@ -282,10 +282,11 @@ std::string MeshPacketSerializer::JsonSerialize(const meshtastic_MeshPacket *mp,
|
||||
|
||||
auto addToRoute = [](JsonArray *route, NodeNum num) {
|
||||
char long_name[40] = "Unknown";
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(num);
|
||||
bool name_known = node ? node->has_user : false;
|
||||
if (name_known)
|
||||
memcpy(long_name, node->user.long_name, sizeof(long_name));
|
||||
meshtastic_NodeDetail *node = nodeDB->getMeshNode(num);
|
||||
if (node && detailHasFlag(*node, NODEDETAIL_FLAG_HAS_USER)) {
|
||||
strncpy(long_name, node->long_name, sizeof(long_name) - 1);
|
||||
long_name[sizeof(long_name) - 1] = '\0';
|
||||
}
|
||||
route->add(long_name);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user