mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-31 17:26:12 +00:00
feat(nav): standalone Compass in Tools
Tools › Compass shows the device's course over ground (from the GPS COG ring — no magnetometer) as a rotating arrow in a ring with a north tick, plus a large degrees + cardinal readout. "No GPS fix" without a fix; "move to set heading" when stationary (course undefined). Reuses UITask::currentCourse, so it works without recording a trail. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
|||||||
|
#pragma once
|
||||||
|
// Tools › Compass. A standalone heading view: shows the device's course over
|
||||||
|
// ground (derived from GPS movement — there is no magnetometer) as a rotating
|
||||||
|
// arrow plus a numeric degrees + cardinal readout. When standing still the
|
||||||
|
// heading is undefined, so it shows a hint to move.
|
||||||
|
//
|
||||||
|
// Reuses UITask's COG ring (currentCourse) — works whether or not a trail is
|
||||||
|
// being recorded.
|
||||||
|
|
||||||
|
#include "../GeoUtils.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
class CompassScreen : public UIScreen {
|
||||||
|
UITask* _task;
|
||||||
|
|
||||||
|
static bool gpsValid() {
|
||||||
|
#if ENV_INCLUDE_GPS == 1
|
||||||
|
LocationProvider* loc = sensors.getLocationProvider();
|
||||||
|
return loc && loc->isValid();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void drawLine(DisplayDriver& d, int x0, int y0, int x1, int y1) {
|
||||||
|
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||||
|
int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||||
|
int err = dx + dy;
|
||||||
|
while (true) {
|
||||||
|
d.fillRect(x0, y0, 1, 1);
|
||||||
|
if (x0 == x1 && y0 == y1) break;
|
||||||
|
int e2 = err * 2;
|
||||||
|
if (e2 >= dy) { err += dy; x0 += sx; }
|
||||||
|
if (e2 <= dx) { err += dx; y0 += sy; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CompassScreen(UITask* task) : _task(task) {}
|
||||||
|
void enter() {}
|
||||||
|
|
||||||
|
int render(DisplayDriver& display) override {
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setColor(DisplayDriver::LIGHT);
|
||||||
|
display.drawTextCentered(display.width() / 2, 0, "COMPASS");
|
||||||
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
|
const int hdr = display.headerH();
|
||||||
|
const int cx = display.width() / 2;
|
||||||
|
// Reserve a size-2 row at the bottom for the numeric readout.
|
||||||
|
display.setTextSize(2);
|
||||||
|
const int bigH = display.getLineHeight();
|
||||||
|
display.setTextSize(1);
|
||||||
|
const int top = hdr + 2;
|
||||||
|
const int bottom = display.height() - bigH - 2;
|
||||||
|
const int cy = (top + bottom) / 2;
|
||||||
|
int r = ((bottom - top) / 2);
|
||||||
|
int rx = (display.width() / 2) - 4;
|
||||||
|
if (rx < r) r = rx;
|
||||||
|
if (r < 6) r = 6;
|
||||||
|
|
||||||
|
if (!gpsValid()) {
|
||||||
|
display.drawTextCentered(cx, cy - display.getLineHeight() / 2, "No GPS fix");
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cog;
|
||||||
|
bool have = _task->currentCourse(cog);
|
||||||
|
|
||||||
|
// Compass ring ticks: N/E/S/W as short marks; N drawn as a filled wedge top.
|
||||||
|
display.drawRect(cx - r, cy - r, 2 * r + 1, 2 * r + 1); // bounding box stands in for a ring
|
||||||
|
display.drawTextCentered(cx, cy - r - 1, "N");
|
||||||
|
|
||||||
|
if (!have) {
|
||||||
|
display.drawTextCentered(cx, cy - display.getLineHeight() / 2, "move to");
|
||||||
|
display.drawTextCentered(cx, cy + display.getLineHeight() / 2, "set heading");
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrow from centre toward the course (0° = up = north, clockwise).
|
||||||
|
float rad = cog * (float)M_PI / 180.0f;
|
||||||
|
int ex = cx + (int)(sinf(rad) * (r - 2));
|
||||||
|
int ey = cy - (int)(cosf(rad) * (r - 2));
|
||||||
|
drawLine(display, cx, cy, ex, ey);
|
||||||
|
// Small arrowhead: two short lines back from the tip at ±150°.
|
||||||
|
for (int da = -25; da <= 25; da += 50) {
|
||||||
|
float a = rad + (float)M_PI + da * (float)M_PI / 180.0f;
|
||||||
|
int hx = ex + (int)(sinf(a) * 4);
|
||||||
|
int hy = ey - (int)(cosf(a) * 4);
|
||||||
|
drawLine(display, ex, ey, hx, hy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Numeric readout below the ring.
|
||||||
|
char buf[16];
|
||||||
|
snprintf(buf, sizeof(buf), "%d %s", cog, geo::bearingCardinal(cog));
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.drawTextCentered(cx, bottom + 1, buf);
|
||||||
|
display.setTextSize(1);
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handleInput(char c) override {
|
||||||
|
if (c == KEY_CANCEL || c == KEY_CONTEXT_MENU) { _task->gotoToolsScreen(); return true; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -6,7 +6,7 @@ class ToolsScreen : public UIScreen {
|
|||||||
UITask* _task;
|
UITask* _task;
|
||||||
int _sel;
|
int _sel;
|
||||||
|
|
||||||
static const int ITEM_COUNT = 5;
|
static const int ITEM_COUNT = 6;
|
||||||
static const char* ITEMS[ITEM_COUNT];
|
static const char* ITEMS[ITEM_COUNT];
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -45,8 +45,9 @@ public:
|
|||||||
if (_sel == 2) { _task->gotoNearbyScreen(); return true; }
|
if (_sel == 2) { _task->gotoNearbyScreen(); return true; }
|
||||||
if (_sel == 3) { _task->gotoAutoAdvertScreen(); return true; }
|
if (_sel == 3) { _task->gotoAutoAdvertScreen(); return true; }
|
||||||
if (_sel == 4) { _task->gotoTrailScreen(); return true; }
|
if (_sel == 4) { _task->gotoTrailScreen(); return true; }
|
||||||
|
if (_sel == 5) { _task->gotoCompassScreen(); return true; }
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const char* ToolsScreen::ITEMS[5] = { "Ringtone Editor", "Auto-Reply Bot", "Nearby Nodes", "Auto-Advert", "Trail" };
|
const char* ToolsScreen::ITEMS[6] = { "Ringtone Editor", "Auto-Reply Bot", "Nearby Nodes", "Auto-Advert", "Trail", "Compass" };
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ static const int QUICK_MSGS_MAX = 10;
|
|||||||
#include "DashboardConfigScreen.h"
|
#include "DashboardConfigScreen.h"
|
||||||
#include "AutoAdvertScreen.h"
|
#include "AutoAdvertScreen.h"
|
||||||
#include "TrailScreen.h"
|
#include "TrailScreen.h"
|
||||||
|
#include "CompassScreen.h"
|
||||||
#include "ToolsScreen.h"
|
#include "ToolsScreen.h"
|
||||||
|
|
||||||
#ifndef BATT_MIN_MILLIVOLTS
|
#ifndef BATT_MIN_MILLIVOLTS
|
||||||
@@ -1189,6 +1190,7 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
|||||||
dashboard_config = new DashboardConfigScreen(this, node_prefs);
|
dashboard_config = new DashboardConfigScreen(this, node_prefs);
|
||||||
auto_advert_screen = new AutoAdvertScreen(this, node_prefs);
|
auto_advert_screen = new AutoAdvertScreen(this, node_prefs);
|
||||||
trail_screen = new TrailScreen(this, &_trail);
|
trail_screen = new TrailScreen(this, &_trail);
|
||||||
|
compass_screen = new CompassScreen(this);
|
||||||
applyBrightness();
|
applyBrightness();
|
||||||
applyFont();
|
applyFont();
|
||||||
applyRotation();
|
applyRotation();
|
||||||
@@ -1230,6 +1232,11 @@ void UITask::gotoTrailScreen() {
|
|||||||
setCurrScreen(trail_screen);
|
setCurrScreen(trail_screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UITask::gotoCompassScreen() {
|
||||||
|
((CompassScreen*)compass_screen)->enter();
|
||||||
|
setCurrScreen(compass_screen);
|
||||||
|
}
|
||||||
|
|
||||||
void UITask::gotoAutoAdvertScreen() {
|
void UITask::gotoAutoAdvertScreen() {
|
||||||
((AutoAdvertScreen*)auto_advert_screen)->enter();
|
((AutoAdvertScreen*)auto_advert_screen)->enter();
|
||||||
setCurrScreen(auto_advert_screen);
|
setCurrScreen(auto_advert_screen);
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ class UITask : public AbstractUITask {
|
|||||||
UIScreen* dashboard_config;
|
UIScreen* dashboard_config;
|
||||||
UIScreen* auto_advert_screen;
|
UIScreen* auto_advert_screen;
|
||||||
UIScreen* trail_screen;
|
UIScreen* trail_screen;
|
||||||
|
UIScreen* compass_screen;
|
||||||
UIScreen* curr;
|
UIScreen* curr;
|
||||||
CayenneLPP _dash_lpp;
|
CayenneLPP _dash_lpp;
|
||||||
TrailStore _trail;
|
TrailStore _trail;
|
||||||
@@ -144,6 +145,7 @@ public:
|
|||||||
void gotoDashboardConfig();
|
void gotoDashboardConfig();
|
||||||
void gotoAutoAdvertScreen();
|
void gotoAutoAdvertScreen();
|
||||||
void gotoTrailScreen();
|
void gotoTrailScreen();
|
||||||
|
void gotoCompassScreen();
|
||||||
TrailStore& trail() { return _trail; }
|
TrailStore& trail() { return _trail; }
|
||||||
WaypointStore& waypoints() { return _waypoints; }
|
WaypointStore& waypoints() { return _waypoints; }
|
||||||
void saveWaypoints(); // persist the table to /waypoints
|
void saveWaypoints(); // persist the table to /waypoints
|
||||||
|
|||||||
Reference in New Issue
Block a user