Skip to content

feat(radar): add radar minimap overlay to the Distance/Bearings frame#10224

Open
egorsiniaev wants to merge 51 commits into
meshtastic:developfrom
egorsiniaev:feat/radar-node-view
Open

feat(radar): add radar minimap overlay to the Distance/Bearings frame#10224
egorsiniaev wants to merge 51 commits into
meshtastic:developfrom
egorsiniaev:feat/radar-node-view

Conversation

@egorsiniaev

@egorsiniaev egorsiniaev commented Apr 21, 2026

Copy link
Copy Markdown

What this PR does

Adds a radar minimap view as an alternative rendering of the existing Distance/Bearings frame — no new screen slot in the rotation.

From the node-list long-press menu, a new "Tracking View" picker (clock-face style) lets the user switch between the regular Bearings list and the Radar overlay. While in radar mode, long-pressing opens a radar-specific menu (Tracking View, N-UP/HDG-UP toggle, Favorites Only toggle, Zoom In/Out). Both choices are persistent across reboots (bearings_view_radar and radar_favorites_only in DeviceUIConfig, proto fields 20 and 21).

Radar layout

  • Left panel: up to 5 closest nodes — per-row marker symbol, short name, distance
  • Right panel: circular minimap with 3 range rings and 2 px padding so the circle never touches the display edge
  • Header: shows the current outer-ring range as Radar 5km / Radar 1.5mi (units follow display.units)
  • Own position is a single pixel at the centre
  • Five unique markers (■ + × □ ◆) — sort-position-based, so the dot on the radar always matches the row in the list
  • Heading-up when IMU (BMX160/RAK12034) is present; falls back to north-up
  • Favorites-only filter for use at large gatherings — toggled from the radar long-press menu
  • No GPS fix state shows a clear message instead of an empty radar
  • No impact on e-ink devices — the entire bearings/distance frame this view lives in is already inside a #ifndef USE_EINK block in NodeListRenderer.cpp, so the radar code isn't compiled in for e-ink builds

Files changed

  • src/graphics/draw/RadarRenderer.cpp/.h — radar overlay renderer; owns its own header so the title can carry the current range
  • src/graphics/draw/NodeListRenderer.cpp — delegates to RadarRenderer::drawRadarOverlay when bearings_view_radar is set
  • src/graphics/draw/MenuHandler.cpp/.h — adds trackingViewPicker() and radarBearingsMenu(); wires "Tracking View" into the node-list menu
  • src/graphics/Screen.cpp — routes long-press on the bearings frame to radarBearingsMenu when radar is active
  • src/mesh/generated/meshtastic/device_ui.pb.h — adds bool bearings_view_radar (field 20) and bool radar_favorites_only (field 21), both backwards-compatible

🤝 Attestations

  • I have tested that my proposed changes behave as described.
  • I have tested that my proposed changes do not cause any obvious regressions on the following devices:
    • Heltec (Lora32) V3
    • lilygo lora32 v2.1
    • LilyGo T-Deck
    • LilyGo T-Beam
    • RAK WisBlock 4631
    • Seeed Studio T-1000E tracker card
    • Other (please specify below)

Note: Happy to get help testing on other hardware from the community.

image 20260603_083837

Video: https://photos.app.goo.gl/Tj3y2PKypFPwczE58

Summary by CodeRabbit

  • New Features
    • Added a radar-style location overlay with North-up vs Heading-up, zoom in/out, and favorites-only filtering, including a side list of active nearby nodes.
    • Added a “Tracking View” chooser to switch between bearings and radar presentation, with a return-to-menu “Back” flow.
  • Bug Fixes
    • Improved menu navigation so the correct bearings/radar view opens from the nodelist/location screens based on the selected tracking mode (including the GPS-aware option).

@CLAassistant

CLAassistant commented Apr 21, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot added needs-review Needs human review enhancement New feature or request labels Apr 21, 2026
@egorsiniaev

Copy link
Copy Markdown
Author

CLA assistant check Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.You have signed the CLA already but the status is still pending? Let us recheck it.

I have 500 Internal Server Error after signing it.

@caveman99

Copy link
Copy Markdown
Member

We have 3 nodelist displays with different additonal things. This adds a 4th list with a graphical representation. Also overlaps with the functionality of the GPS Compass. Maybe we can have this panel as an option instead of the GPS Compass or even merge it with the compass.

@caveman99

Copy link
Copy Markdown
Member

I have 500 Internal Server Error after signing it.

The CLA server seems to have issues. Can you try again maybe tomorrow?

@egorsiniaev

egorsiniaev commented Apr 21, 2026

Copy link
Copy Markdown
Author

We have 3 nodelist displays with different additonal things. This adds a 4th list with a graphical representation. Also overlaps with the functionality of the GPS Compass. Maybe we can have this panel as an option instead of the GPS Compass or even merge it with the compass.

I thought about it, but because this is my first PR and contribution - to suggest such merge without long term community vision is a strange step. That's why I decided to have clear separate section that easy to review + easy to disable if people don't need it.

@caveman99

Copy link
Copy Markdown
Member

Problem is, we rather would like to make the interface more clear, not pile on it. I see the value in your contribution, i just think i t would really benefit the existing screens rather than open up a new one.

@fifieldt

Copy link
Copy Markdown
Member

This is cool :) I want to see how it looks on a larger screen, like the sensecap indicator - will try when I get time.

Also, appreciate your collaborative approach.

@egorsiniaev egorsiniaev changed the title feat(radar): add radar minimap screen showing nearby nodes by bearing and distance feat(radar): add radar minimap overlay to the compass/position screen Apr 21, 2026
@egorsiniaev

egorsiniaev commented Apr 21, 2026

Copy link
Copy Markdown
Author

Problem is, we rather would like to make the interface more clear, not pile on it. I see the value in your contribution, i just think i t would really benefit the existing screens rather than open up a new one.

I updated. Removed separate Radar view and add option to switch Compass mode Radar mode. Plus changed a bit orientation (nodes on the left, radar on the right) to match compass layout.

Please take a look. Should I need to update screenshots? They are still showing old layout.

@egorsiniaev

Copy link
Copy Markdown
Author

This is cool :) I want to see how it looks on a larger screen, like the sensecap indicator - will try when I get time.

Also, appreciate your collaborative approach.

Will appreciate for checking it. I have no such devices yet.

@egorsiniaev

Copy link
Copy Markdown
Author

Hey @caveman99 Thanks for your suggestion. I'v made changes and pushed them. Today made photos and record video for better representation. In short - I extended Compass, add long-press menu to switch to the radar view + changed a bit layout to match compass. Please take a look and let me know if anything else is missing.

@egorsiniaev

egorsiniaev commented May 4, 2026

Copy link
Copy Markdown
Author

Hey @caveman99 @fifieldt
Could you please tell me - how to move forward with this PR? There are no reviewers added automatically. It's waiting about 2 weeks. Maybe I missed something to do or somebody need to approve CI to start checking changes.
Thank you.

@Xaositek

Xaositek commented May 7, 2026

Copy link
Copy Markdown
Contributor

Reviewing the video - I do like the idea and concept. It's well executed and provides a display not so dissimilar to what is in InkHUD. Need to load it up on a couple devices to give it a run through.

@Xaositek Xaositek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested it out on my Cardputer and I can't see it actually working. I tried Zooming In and Out, verified I have a GPS lock, and nodes are pretty close by.

I feel like we need some refinements like "What is my current Zoom level?", "Do I have GPS lock"?

Also the toggle between Compass and Radar likely needs to be similar to what we have on the Clock screen where you click on "Clock Face" and then choose Digital or Analog.

@Xaositek

Xaositek commented May 7, 2026

Copy link
Copy Markdown
Contributor
IMG_3250.mov

Video of my attempt

@HarukiToreda

Copy link
Copy Markdown
Contributor

I had a similar idea while creating the distance and bearing screen, I ultimately opted to not implement due to the following:

  • Adds another screen frame into an already extensive list of frames users have to cycle through, this was prior to the action menus (I see you solved this nicely using action menu but I would suggest making it swap the distance and bearing screen instead of the position screen since the position screen is not related to tracking nodes)

  • Showing the closest random nodes does not add much value other than a wow factor, something the position and bearing screen already do with arrows and distance markers, (However if you can limit it to show favorite nodes only, now that adds a lot of value for people since that is a deliberate action useful for large events and gatherings "Favorite Radar View")

  • Markers to differentiate the nodes was very limited (I see from the video you're reusing the x for the first 3, would you be able to make them different?)

…stance

Adds a new RadarRenderer that draws a circular radar view with three range
rings. The user's node sits at the centre; other nodes with valid GPS
positions are plotted as 3×3 squares at their true bearing and
proportional distance. A "N" indicator marks north, the info panel to
the right shows the current scale, total node count, and the name and
distance of the closest node.

The screen is registered for non-E-Ink builds that have GPS (HAS_GPS)
and can be hidden via the existing hiddenFrames mechanism.
When BMX160 (RAK12034) is connected, Screen::setHeading() is updated
by BMX160Sensor::runOnce() after tilt-compensated compass fusion.
The radar now reads that heading via screen->hasHeading()/getHeading()
and rotates all node bearings and the "N" indicator accordingly, so
the direction the device faces is always at the top of the display
(heading-up mode).

Falls back to GPS-estimated track heading, then north-up when neither
is available. The info panel shows "HDG-UP" or "N-UP" so the current
mode is always visible.
- Centre the radar circle horizontally, filling the full content height
  (radius=27 on 128×64, scales up on larger displays)
- Replace the wide info panel with a compact ring legend to the right:
  three stacked distance labels (outer/middle/inner ring scale)
  plus a small "IMU" indicator when the BMX160 is active
- Remove per-screen closest-node text — node positions on the rings
  now carry the distance information visually
… long-press menu

1. Scale table — all values now multiples of 3 so ring labels (scale/3
   and scale*2/3) are always whole numbers (e.g. 30/60/90m, 100/200/300m).

2. Layout — radar shifted to the left edge; right panel shows three ring
   scale labels, closest node short name, distance, and a node-count +
   orientation badge (HDG / N^) in the bottom row.

3. Icon — replaced shared icon_compass with a new icon_radar (concentric
   rings with centre dot) so the radar has its own menu indicator.

4. Menu toggle — "Show/Hide Radar" added to the Show/Hide Frames menu
   via the existing toggleFrameVisibility / isFrameHidden pattern.

5. Long-press menu — pressing SELECT on the radar screen opens "Radar
   Options" with: Switch N-UP / HDG-UP (overrides IMU), Zoom In, Zoom Out.
   Zoom is clamped to ±2 steps from auto-scale; state is held in module-
   static variables in RadarRenderer.
Problem: on 128×64 OLED with FONT_SMALL≈13px, the 3 ring-label rows
left no space for node info — name/distance were clipped off screen.

Fix:
- Info panel reduced to 4 rows:
    Row 0  outer ring scale (scale reference)
    Row 1  closest node: name (left) + distance (right-aligned)
    Row 2  2nd closest node: name + distance
    Row 3  node count + HDG/N^ badge
- Entries sorted by distance ascending before drawing so rows 1/2
  always show the two nearest nodes.
- Closest node rendered as a + cross on the radar; all others keep
  the 3×3 filled-square marker. The cross lets the user match the
  top panel row to the correct dot without cluttering the radar with
  text labels.
Each node is assigned a persistent marker shape based on nodeNum % 5:
  0 ■  filled square
  1 +  axis-aligned cross
  2 ×  diagonal X
  3 □  hollow square
  4 ◆  diamond

The same symbol appears on the radar dot AND in the right-panel list,
so the user can visually match any dot on the map to its panel entry
without text labels cluttering the radar.

Info panel:
  Row 0  outer ring scale
  Row 1  [sym] closest node name (left)  distance (right)
  Row 2  [sym] 2nd closest      (left)  distance (right)
  Row 3  node count (left)      orientation badge (right)
Instead of a separate screen slot in the rotation, the radar now lives inside
the existing Position/Compass screen.

- Remove standalone Radar frame and icon from screen rotation
- Add 'Radar View' entry to the position long-press menu (positionBaseMenu)
- Long-press in radar mode opens radarPositionMenu with: Compass View,
  N-UP/HDG-UP toggle, Zoom In, Zoom Out
- UIRenderer::drawCompassAndLocationScreen delegates to
  RadarRenderer::drawRadarOverlay when uiconfig.radar_mode is true
- Radar layout: node list (left) + radar circle (right, 2 px padding)
- radar_mode persisted as bool field 20 in DeviceUIConfig (proto-compatible)
- Removes nodelist_radar from hiddenFrames, framesetInfo.positions, and
  all frame-toggle machinery
@egorsiniaev egorsiniaev force-pushed the feat/radar-node-view branch from 3e7ed0d to 4d6109c Compare June 3, 2026 08:28
@egorsiniaev

Copy link
Copy Markdown
Author

While waiting for PR - I ordered T-Deck and did some tweaks for highres screens:

  • Now it could 5 \ 8 \ 10 nodes depends on screen size
  • Optimised paddings for different screens
  • Added ring labels to show distance. Works only on highres.
20260603_083837

FYI @HarukiToreda @HarukiToreda @fifieldt Let me know what do you think.

@egorsiniaev

Copy link
Copy Markdown
Author

@HarukiToreda @fifieldt Could you take a look on this PRs, guys?

…_SECS

The radar overlay iterated every node in the DB with a stored position and
plotted it, regardless of age. Since NodeDB never time-expires nodes, this
drew "ghosts" at last-known positions of nodes that went offline long ago.

Gate the plot loop on sinceLastSeen() < NUM_ONLINE_SECS (2 hrs), the same
threshold getNumOnlineMeshNodes() uses, so the radar shows only nodes the
rest of the UI also considers online. Lift NUM_ONLINE_SECS from NodeDB.cpp
into NodeDB.h so RadarRenderer can share the single definition.
When no nodes have been heard within NUM_ONLINE_SECS, the radar drew
empty rings with a blank list and no explanation. Render a centered
'No active nodes' message in the list-panel column so the empty state
is intentional and clear, leaving the radar rings intact.
@egorsiniaev

Copy link
Copy Markdown
Author

Hey hey @fifieldt @Xaositek Friendly ping. Please review this PR.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Firmware Size Report

22 targets | vs develop: 22 decreased, net -77,452 (-75.6 KB)

Target Size vs develop
heltec-vision-master-e213-inkhud 2,216,432 📉 -10,928 (-10.7 KB)
elecrow-adv-35-tft 3,407,392 📉 -9,232 (-9.0 KB)
heltec-ht62-esp32c3-sx1262 2,126,432 📉 -8,560 (-8.4 KB)
tlora-c6 2,359,808 📉 -8,496 (-8.3 KB)
wio-e5 234,372 📉 -4,256 (-4.2 KB)
Show 17 more target(s)
Target Size vs develop
rak3172 182,116 📉 -4,180 (-4.1 KB)
rak11200 1,857,776 📉 -2,832 (-2.8 KB)
t-eth-elite 2,488,944 📉 -2,544 (-2.5 KB)
picow 1,242,584 📉 -2,296 (-2.2 KB)
pico 780,424 📉 -2,264 (-2.2 KB)
seeed_xiao_rp2040 778,640 📉 -2,264 (-2.2 KB)
heltec-v3 2,261,872 📉 -2,208 (-2.2 KB)
heltec-v4 2,275,184 📉 -2,112 (-2.1 KB)
rak11310 803,344 📉 -2,080 (-2.0 KB)
rak3312 2,270,336 📉 -1,904 (-1.9 KB)
seeed-xiao-s3 2,274,336 📉 -1,808 (-1.8 KB)
pico2w 1,218,724 📉 -1,792 (-1.8 KB)
pico2 768,040 📉 -1,736 (-1.7 KB)
seeed_xiao_rp2350 766,200 📉 -1,736 (-1.7 KB)
station-g2 2,264,752 📉 -1,600 (-1.6 KB)
station-g3 2,264,752 📉 -1,600 (-1.6 KB)
t-deck-tft 3,809,648 📉 -1,024 (-1.0 KB)

Updated for 065053d

Fixes Trunk Check (clang-format) failure - file was authored with
2-space/attached-brace style; reformat to repo Linux-brace 4-space style.
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ec1a6295-ef88-4c4c-9de5-3953b2bdc644

📥 Commits

Reviewing files that changed from the base of the PR and between b3bf2b9 and 7e99f88.

📒 Files selected for processing (2)
  • src/mesh/NodeDB.cpp
  • src/mesh/NodeDB.h
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/mesh/NodeDB.h
  • src/mesh/NodeDB.cpp

📝 Walkthrough

Walkthrough

This PR adds a radar overlay renderer and menu-driven tracking view controls for the node list screen, and moves the online-node timeout constant into NodeDB.h.

Changes

Radar Overlay Feature

Layer / File(s) Summary
Radar renderer
src/graphics/draw/RadarRenderer.h, src/graphics/draw/RadarRenderer.cpp
Declares and implements radar overlay drawing plus north-up/zoom controls, node plotting, ring scaling, list rendering, connection icon overlay, and empty-state handling.
Screen and menu wiring
src/graphics/draw/NodeListRenderer.cpp, src/graphics/Screen.cpp, src/graphics/draw/UIRenderer.cpp
Switches the location screen to the radar overlay when enabled, routes input to the radar bearings menu, and includes a whitespace-only UIRenderer change.
Tracking view and radar menus
src/graphics/draw/MenuHandler.h, src/graphics/draw/MenuHandler.cpp
Adds the tracking-view picker enum and handlers, persists the bearings/radar selection, adds the radar bearings menu, updates node list menu actions, and dispatches the new picker case.

Node Online Timeout Constant Relocation

Layer / File(s) Summary
Move NUM_ONLINE_SECS
src/mesh/NodeDB.h, src/mesh/NodeDB.cpp
Defines NUM_ONLINE_SECS in the header with a comment and removes the local definition from the implementation file.

Estimated code review effort: 4 (Complex) | ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a radar overlay to the Distance/Bearings frame.
Description check ✅ Passed The description covers the feature, files changed, persistence, and testing attestations, matching the repository's expected PR info.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/graphics/draw/RadarRenderer.cpp (1)

194-229: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Icon-drawing logic duplicated from SharedUIDisplay::drawCommonFooter.

The comment already acknowledges this replicates half of drawCommonFooter's behavior. Since both copies must stay in sync (icon glyph format, scale handling, XBM bit layout), consider extracting a shared drawConnectionIcon(display, x, y, scale) helper in SharedUIDisplay that both call, so future icon-format changes don't need to be applied twice.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/graphics/draw/RadarRenderer.cpp` around lines 194 - 229, The connection
icon drawing logic in drawConnectionIconNoWipe is duplicated from
SharedUIDisplay::drawCommonFooter, so extract the shared icon-rendering behavior
into a reusable helper such as drawConnectionIcon(display, x, y, scale) and have
both drawCommonFooter and drawConnectionIconNoWipe call it. Keep the helper
responsible for the icon glyph, resolution scaling, and XBM bit layout so
changes stay synchronized in one place.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/graphics/draw/RadarRenderer.cpp`:
- Around line 285-293: The early return in RadarRenderer::drawRadar skips the
connection icon, causing it to disappear in the “No GPS fix” state. Update this
branch to preserve the same UI behavior as the other exits by calling
drawConnectionIconNoWipe(display) before returning, matching the rest of the
function’s icon-preservation flow.
- Around line 298-311: The heading fallback in RadarRenderer::draw is using
screen->estimatedHeading() directly as if it were radians, but it returns
degrees and can also yield the -1.0f sentinel when unavailable. Update the
headingRad selection so the GPS fallback branch converts estimatedHeading(myLat,
myLon) with DEG_TO_RAD, and preserve the north-up fallback when no real heading
is available; keep the change localized to the heading/headingUp logic in
RadarRenderer.cpp.

---

Nitpick comments:
In `@src/graphics/draw/RadarRenderer.cpp`:
- Around line 194-229: The connection icon drawing logic in
drawConnectionIconNoWipe is duplicated from SharedUIDisplay::drawCommonFooter,
so extract the shared icon-rendering behavior into a reusable helper such as
drawConnectionIcon(display, x, y, scale) and have both drawCommonFooter and
drawConnectionIconNoWipe call it. Keep the helper responsible for the icon
glyph, resolution scaling, and XBM bit layout so changes stay synchronized in
one place.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3c08e9e0-dff6-475a-a602-b2c64eabe4d1

📥 Commits

Reviewing files that changed from the base of the PR and between 93250d8 and dfc9b13.

⛔ Files ignored due to path filters (1)
  • src/mesh/generated/meshtastic/device_ui.pb.h is excluded by !**/generated/**
📒 Files selected for processing (9)
  • src/graphics/Screen.cpp
  • src/graphics/draw/MenuHandler.cpp
  • src/graphics/draw/MenuHandler.h
  • src/graphics/draw/NodeListRenderer.cpp
  • src/graphics/draw/RadarRenderer.cpp
  • src/graphics/draw/RadarRenderer.h
  • src/graphics/draw/UIRenderer.cpp
  • src/mesh/NodeDB.cpp
  • src/mesh/NodeDB.h
💤 Files with no reviewable changes (1)
  • src/mesh/NodeDB.cpp

Comment thread src/graphics/draw/RadarRenderer.cpp
Comment thread src/graphics/draw/RadarRenderer.cpp Outdated
- Preserve connection icon in 'No GPS fix' early return (was skipped,
  causing the icon to disappear until a fix was acquired).
- Convert GPS estimatedHeading() fallback from degrees to radians and
  honor its -1.0f sentinel, falling back to north-up until a real
  heading exists (matches CompassRenderer).
@egorsiniaev

egorsiniaev commented Jul 6, 2026

Copy link
Copy Markdown
Author

Sorry for the ping again, @Xaositek could you trigger CI again? I got some feedback from CodeRabbit and fixed them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

baseui Issues directly related to BaseUI enhancement New feature or request triaged Reviewed by the team, has enough information and ready to work on now.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants