mirror of
https://github.com/meshtastic/firmware.git
synced 2025-07-31 02:45:41 +00:00
Nodelist with Playa addresses for burningmesh (#7321)
Some checks are pending
CI / setup (check) (push) Waiting to run
CI / setup (esp32) (push) Waiting to run
CI / setup (esp32c3) (push) Waiting to run
CI / setup (esp32c6) (push) Waiting to run
CI / setup (esp32s3) (push) Waiting to run
CI / setup (nrf52840) (push) Waiting to run
CI / setup (rp2040) (push) Waiting to run
CI / setup (stm32) (push) Waiting to run
CI / check (push) Blocked by required conditions
CI / build-esp32 (push) Blocked by required conditions
CI / build-esp32-s3 (push) Blocked by required conditions
CI / build-esp32-c3 (push) Blocked by required conditions
CI / build-esp32-c6 (push) Blocked by required conditions
CI / build-nrf52 (push) Blocked by required conditions
CI / build-rpi2040 (push) Blocked by required conditions
CI / build-stm32 (push) Blocked by required conditions
CI / build-debian-src (push) Waiting to run
CI / package-pio-deps-native-tft (push) Waiting to run
CI / test-native (push) Waiting to run
CI / docker-deb-amd64 (push) Waiting to run
CI / docker-deb-amd64-tft (push) Waiting to run
CI / docker-alp-amd64 (push) Waiting to run
CI / docker-alp-amd64-tft (push) Waiting to run
CI / docker-deb-arm64 (push) Waiting to run
CI / docker-deb-armv7 (push) Waiting to run
CI / gather-artifacts (esp32) (push) Blocked by required conditions
CI / gather-artifacts (esp32c3) (push) Blocked by required conditions
CI / gather-artifacts (esp32c6) (push) Blocked by required conditions
CI / gather-artifacts (esp32s3) (push) Blocked by required conditions
CI / gather-artifacts (nrf52840) (push) Blocked by required conditions
CI / gather-artifacts (rp2040) (push) Blocked by required conditions
CI / gather-artifacts (stm32) (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
CI / release-firmware (esp32) (push) Blocked by required conditions
CI / release-firmware (esp32c3) (push) Blocked by required conditions
CI / release-firmware (esp32c6) (push) Blocked by required conditions
CI / release-firmware (esp32s3) (push) Blocked by required conditions
CI / release-firmware (nrf52840) (push) Blocked by required conditions
CI / release-firmware (rp2040) (push) Blocked by required conditions
CI / release-firmware (stm32) (push) Blocked by required conditions
CI / publish-firmware (push) Blocked by required conditions
Some checks are pending
CI / setup (check) (push) Waiting to run
CI / setup (esp32) (push) Waiting to run
CI / setup (esp32c3) (push) Waiting to run
CI / setup (esp32c6) (push) Waiting to run
CI / setup (esp32s3) (push) Waiting to run
CI / setup (nrf52840) (push) Waiting to run
CI / setup (rp2040) (push) Waiting to run
CI / setup (stm32) (push) Waiting to run
CI / check (push) Blocked by required conditions
CI / build-esp32 (push) Blocked by required conditions
CI / build-esp32-s3 (push) Blocked by required conditions
CI / build-esp32-c3 (push) Blocked by required conditions
CI / build-esp32-c6 (push) Blocked by required conditions
CI / build-nrf52 (push) Blocked by required conditions
CI / build-rpi2040 (push) Blocked by required conditions
CI / build-stm32 (push) Blocked by required conditions
CI / build-debian-src (push) Waiting to run
CI / package-pio-deps-native-tft (push) Waiting to run
CI / test-native (push) Waiting to run
CI / docker-deb-amd64 (push) Waiting to run
CI / docker-deb-amd64-tft (push) Waiting to run
CI / docker-alp-amd64 (push) Waiting to run
CI / docker-alp-amd64-tft (push) Waiting to run
CI / docker-deb-arm64 (push) Waiting to run
CI / docker-deb-armv7 (push) Waiting to run
CI / gather-artifacts (esp32) (push) Blocked by required conditions
CI / gather-artifacts (esp32c3) (push) Blocked by required conditions
CI / gather-artifacts (esp32c6) (push) Blocked by required conditions
CI / gather-artifacts (esp32s3) (push) Blocked by required conditions
CI / gather-artifacts (nrf52840) (push) Blocked by required conditions
CI / gather-artifacts (rp2040) (push) Blocked by required conditions
CI / gather-artifacts (stm32) (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
CI / release-firmware (esp32) (push) Blocked by required conditions
CI / release-firmware (esp32c3) (push) Blocked by required conditions
CI / release-firmware (esp32c6) (push) Blocked by required conditions
CI / release-firmware (esp32s3) (push) Blocked by required conditions
CI / release-firmware (nrf52840) (push) Blocked by required conditions
CI / release-firmware (rp2040) (push) Blocked by required conditions
CI / release-firmware (stm32) (push) Blocked by required conditions
CI / publish-firmware (push) Blocked by required conditions
* Move BRC address code to a .cpp file since it will be used from multiple places. * Nodelist with addresses * Burning Man icon for BRC page * Draw last-seen time instead of compass This is likely more important - don't want to wander to a device that is off. * Add the bearing list back to the cycled pages on oled devices * Add bearings screen into the carousel of rotated screens * trunk fmt * fix build errors caused by trunk fmt * correct swapped screen positions for BRC vs bearings display * don't show playa position if unknown
This commit is contained in:
parent
77dd73fd23
commit
90385ab0cb
152
src/graphics/BRC.cpp
Normal file
152
src/graphics/BRC.cpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
#include "BRC.h"
|
||||||
|
#include "GPSStatus.h"
|
||||||
|
#include "gps/GeoCoord.h"
|
||||||
|
#include "graphics/Screen.h"
|
||||||
|
|
||||||
|
using namespace meshtastic;
|
||||||
|
|
||||||
|
const int32_t BRC_LATI = (40.786958 * 1e7);
|
||||||
|
const int32_t BRC_LONI = (-119.202994 * 1e7);
|
||||||
|
const double BRC_LATF = 40.786958;
|
||||||
|
const double BRC_LONF = -119.202994;
|
||||||
|
const double BRC_NOON = 1.5;
|
||||||
|
const double RAD_TO_HOUR = (6.0 / 3.14159);
|
||||||
|
const double METER_TO_FEET = 3.28084;
|
||||||
|
const double FEET_TO_METER = 1.0 / METER_TO_FEET;
|
||||||
|
|
||||||
|
// Pre-calculated street data for performance
|
||||||
|
struct StreetInfo {
|
||||||
|
float center;
|
||||||
|
float width;
|
||||||
|
const char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
# python code to generate the StreetInfo
|
||||||
|
|
||||||
|
esp_center = 2500
|
||||||
|
street_info = [
|
||||||
|
# name, width, preceeding block depth
|
||||||
|
('Esp', 40, 60), # block size is fake
|
||||||
|
('A', 30, 400),
|
||||||
|
('B', 30, 250),
|
||||||
|
('C', 30, 250),
|
||||||
|
('D', 30, 250),
|
||||||
|
('E', 40, 250),
|
||||||
|
('F', 30, 450), # E-F block is exra deep
|
||||||
|
('G', 30, 250),
|
||||||
|
('H', 30, 250),
|
||||||
|
('I', 30, 250),
|
||||||
|
('J', 30, 150),
|
||||||
|
('K', 50, 150),
|
||||||
|
]
|
||||||
|
|
||||||
|
street_center = esp_center - street_info[0][1] //2 - street_info[0][2]
|
||||||
|
last_center = esp_center
|
||||||
|
for (name, street_width, block_width) in street_info:
|
||||||
|
offset = (street_width + block_width) // 2
|
||||||
|
street_center += street_width //2 + block_width
|
||||||
|
|
||||||
|
dia = street_center * 2
|
||||||
|
dist = street_center - last_center
|
||||||
|
|
||||||
|
print(f"{{{street_center}, {offset}, \"{name}\"}},\t// +{dist}ft\tdia: {dia:,}ft")
|
||||||
|
|
||||||
|
last_center = street_center
|
||||||
|
street_center += street_width //2
|
||||||
|
|
||||||
|
street_center += 50 # extra buffer after the edge of k to include walk-in camping parking
|
||||||
|
print(f"{{{street_center}, 0, nullptr}},\t// +{street_center-last_center}ft")
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const StreetInfo streets[] = {
|
||||||
|
{2500, 50, "Esp"}, // +0ft dia: 5,000ft
|
||||||
|
{2935, 215, "A"}, // +435ft dia: 5,870ft
|
||||||
|
{3215, 140, "B"}, // +280ft dia: 6,430ft
|
||||||
|
{3495, 140, "C"}, // +280ft dia: 6,990ft
|
||||||
|
{3775, 140, "D"}, // +280ft dia: 7,550ft
|
||||||
|
{4060, 145, "E"}, // +285ft dia: 8,120ft
|
||||||
|
{4545, 240, "F"}, // +485ft dia: 9,090ft
|
||||||
|
{4825, 140, "G"}, // +280ft dia: 9,650ft
|
||||||
|
{5105, 140, "H"}, // +280ft dia: 10,210ft
|
||||||
|
{5385, 140, "I"}, // +280ft dia: 10,770ft
|
||||||
|
{5565, 90, "J"}, // +180ft dia: 11,130ft
|
||||||
|
{5755, 100, "K"}, // +190ft dia: 11,510ft
|
||||||
|
{5830, 0, nullptr}, // +75ft
|
||||||
|
};
|
||||||
|
|
||||||
|
BRCAddress::BRCAddress(int32_t lat, int32_t lon)
|
||||||
|
{
|
||||||
|
bearing = GeoCoord::bearing(BRC_LATF, BRC_LONF, DegD(lat), DegD(lon)) * RAD_TO_HOUR;
|
||||||
|
bearing += 12.0 - BRC_NOON;
|
||||||
|
while (bearing > 12.0) {
|
||||||
|
bearing -= 12.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In imperial units because that is how golden spike data is provided.
|
||||||
|
distance = GeoCoord::latLongToMeter(BRC_LATF, BRC_LONF, DegD(lat), DegD(lon)) * METER_TO_FEET;
|
||||||
|
};
|
||||||
|
|
||||||
|
int BRCAddress::radial(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t hour = (uint8_t)(bearing);
|
||||||
|
uint8_t minute = (uint8_t)((bearing - hour) * 60.0);
|
||||||
|
hour %= 12;
|
||||||
|
if (hour == 0) {
|
||||||
|
hour = 12;
|
||||||
|
}
|
||||||
|
return snprintf(buf, len, "%d:%02d", hour, minute);
|
||||||
|
};
|
||||||
|
|
||||||
|
int BRCAddress::annular(char *buf, size_t len, bool noUnit)
|
||||||
|
{
|
||||||
|
const char *unit = "m";
|
||||||
|
float unitMultiplier = FEET_TO_METER;
|
||||||
|
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
||||||
|
unitMultiplier = 1.0;
|
||||||
|
unit = "ft";
|
||||||
|
}
|
||||||
|
if (noUnit)
|
||||||
|
unit = "";
|
||||||
|
|
||||||
|
if (bearing > 1.75 && bearing < 10.25) {
|
||||||
|
const char *street = nullptr;
|
||||||
|
float dist = 0;
|
||||||
|
// Find the appropriate street based on distance
|
||||||
|
for (const auto &s : streets) {
|
||||||
|
if (distance > s.center - s.width) {
|
||||||
|
street = s.name;
|
||||||
|
dist = distance - s.center;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (street) {
|
||||||
|
return snprintf(buf, len, "%s %d%s", street, int(dist * unitMultiplier), unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return snprintf(buf, len, "%d%s", int(distance * unitMultiplier), unit);
|
||||||
|
};
|
||||||
|
|
||||||
|
int BRCAddress::full(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
auto l = radial(buf, len - 4);
|
||||||
|
buf += l;
|
||||||
|
*(buf++) = ' ';
|
||||||
|
*(buf++) = '&';
|
||||||
|
*(buf++) = ' ';
|
||||||
|
buf += annular(buf, len - l - 4, false);
|
||||||
|
buf[l] = 0; // always null terminated
|
||||||
|
return l;
|
||||||
|
};
|
||||||
|
|
||||||
|
int BRCAddress::compact(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
auto l = radial(buf, len - 2);
|
||||||
|
buf += l;
|
||||||
|
*(buf++) = '&';
|
||||||
|
buf += annular(buf, len - l - 2, true);
|
||||||
|
buf[l] = 0; // always null terminated
|
||||||
|
return l;
|
||||||
|
};
|
@ -1,147 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "GPSStatus.h"
|
// For size_t/int32_t types on some platforms.
|
||||||
#include "gps/GeoCoord.h"
|
#include <cstdint>
|
||||||
#include "graphics/Screen.h"
|
// For size_t
|
||||||
|
#include <cstddef>
|
||||||
using namespace meshtastic;
|
|
||||||
|
|
||||||
const int32_t BRC_LATI = (40.786958 * 1e7);
|
|
||||||
const int32_t BRC_LONI = (-119.202994 * 1e7);
|
|
||||||
const double BRC_LATF = 40.786958;
|
|
||||||
const double BRC_LONF = -119.202994;
|
|
||||||
const double BRC_NOON = 1.5;
|
|
||||||
const double RAD_TO_HOUR = (6.0 / 3.14159);
|
|
||||||
const double METER_TO_FEET = 3.28084;
|
|
||||||
const double FEET_TO_METER = 1.0 / METER_TO_FEET;
|
|
||||||
|
|
||||||
// Pre-calculated street data for performance
|
|
||||||
struct StreetInfo {
|
|
||||||
float center;
|
|
||||||
float width;
|
|
||||||
const char *name;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
# python code to generate the StreetInfo
|
|
||||||
|
|
||||||
esp_center = 2500
|
|
||||||
street_info = [
|
|
||||||
# name, width, preceeding block depth
|
|
||||||
('Esp', 40, 60), # block size is fake
|
|
||||||
('A', 30, 400),
|
|
||||||
('B', 30, 250),
|
|
||||||
('C', 30, 250),
|
|
||||||
('D', 30, 250),
|
|
||||||
('E', 40, 250),
|
|
||||||
('F', 30, 450), # E-F block is exra deep
|
|
||||||
('G', 30, 250),
|
|
||||||
('H', 30, 250),
|
|
||||||
('I', 30, 250),
|
|
||||||
('J', 30, 150),
|
|
||||||
('K', 50, 150),
|
|
||||||
]
|
|
||||||
|
|
||||||
street_center = esp_center - street_info[0][1] //2 - street_info[0][2]
|
|
||||||
last_center = esp_center
|
|
||||||
for (name, street_width, block_width) in street_info:
|
|
||||||
offset = (street_width + block_width) // 2
|
|
||||||
street_center += street_width //2 + block_width
|
|
||||||
|
|
||||||
dia = street_center * 2
|
|
||||||
dist = street_center - last_center
|
|
||||||
|
|
||||||
print(f"{{{street_center}, {offset}, \"{name}\"}},\t// +{dist}ft\tdia: {dia:,}ft")
|
|
||||||
|
|
||||||
last_center = street_center
|
|
||||||
street_center += street_width //2
|
|
||||||
|
|
||||||
street_center += 50 # extra buffer after the edge of k to include walk-in camping parking
|
|
||||||
print(f"{{{street_center}, 0, nullptr}},\t// +{street_center-last_center}ft")
|
|
||||||
*/
|
|
||||||
|
|
||||||
static const StreetInfo streets[] = {
|
|
||||||
{2500, 50, "Esp"}, // +0ft dia: 5,000ft
|
|
||||||
{2935, 215, "A"}, // +435ft dia: 5,870ft
|
|
||||||
{3215, 140, "B"}, // +280ft dia: 6,430ft
|
|
||||||
{3495, 140, "C"}, // +280ft dia: 6,990ft
|
|
||||||
{3775, 140, "D"}, // +280ft dia: 7,550ft
|
|
||||||
{4060, 145, "E"}, // +285ft dia: 8,120ft
|
|
||||||
{4545, 240, "F"}, // +485ft dia: 9,090ft
|
|
||||||
{4825, 140, "G"}, // +280ft dia: 9,650ft
|
|
||||||
{5105, 140, "H"}, // +280ft dia: 10,210ft
|
|
||||||
{5385, 140, "I"}, // +280ft dia: 10,770ft
|
|
||||||
{5565, 90, "J"}, // +180ft dia: 11,130ft
|
|
||||||
{5755, 100, "K"}, // +190ft dia: 11,510ft
|
|
||||||
{5830, 0, nullptr}, // +75ft
|
|
||||||
};
|
|
||||||
|
|
||||||
class BRCAddress
|
class BRCAddress
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BRCAddress(int32_t lat, int32_t lon)
|
BRCAddress(int32_t lat, int32_t lon);
|
||||||
{
|
|
||||||
bearing = GeoCoord::bearing(BRC_LATF, BRC_LONF, DegD(lat), DegD(lon)) * RAD_TO_HOUR;
|
|
||||||
bearing += 12.0 - BRC_NOON;
|
|
||||||
while (bearing > 12.0) {
|
|
||||||
bearing -= 12.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In imperial units because that is how golden spike data is provided.
|
int radial(char *buf, size_t len);
|
||||||
distance = GeoCoord::latLongToMeter(BRC_LATF, BRC_LONF, DegD(lat), DegD(lon)) * METER_TO_FEET;
|
int annular(char *buf, size_t len, bool noUnit);
|
||||||
};
|
int full(char *buf, size_t len);
|
||||||
|
int compact(char *buf, size_t len);
|
||||||
int radial(char *buf, size_t len)
|
|
||||||
{
|
|
||||||
uint8_t hour = (uint8_t)(bearing);
|
|
||||||
uint8_t minute = (uint8_t)((bearing - hour) * 60.0);
|
|
||||||
hour %= 12;
|
|
||||||
if (hour == 0) {
|
|
||||||
hour = 12;
|
|
||||||
}
|
|
||||||
return snprintf(buf, len, "%d:%02d", hour, minute);
|
|
||||||
};
|
|
||||||
|
|
||||||
int annular(char *buf, size_t len)
|
|
||||||
{
|
|
||||||
const char *unit = "m";
|
|
||||||
float unitMultiplier = FEET_TO_METER;
|
|
||||||
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
|
||||||
unitMultiplier = 1.0;
|
|
||||||
unit = "ft";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bearing > 1.75 && bearing < 10.25) {
|
|
||||||
const char *street = nullptr;
|
|
||||||
float dist = 0;
|
|
||||||
// Find the appropriate street based on distance
|
|
||||||
for (const auto &s : streets) {
|
|
||||||
if (distance > s.center - s.width) {
|
|
||||||
street = s.name;
|
|
||||||
dist = distance - s.center;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (street) {
|
|
||||||
return snprintf(buf, len, "%s %d%s", street, int(dist * unitMultiplier), unit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return snprintf(buf, len, "%d%s", int(distance * unitMultiplier), unit);
|
|
||||||
};
|
|
||||||
|
|
||||||
int full(char *buf, size_t len)
|
|
||||||
{
|
|
||||||
auto l = radial(buf, len - 4);
|
|
||||||
buf += l;
|
|
||||||
*(buf++) = ' ';
|
|
||||||
*(buf++) = '&';
|
|
||||||
*(buf++) = ' ';
|
|
||||||
buf += annular(buf, len - l - 4);
|
|
||||||
buf[l] = 0; // always null terminated
|
|
||||||
return l;
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float bearing;
|
float bearing;
|
||||||
|
@ -905,7 +905,7 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
|
|
||||||
// Show detailed node views only on E-Ink builds
|
// Show detailed node views only on E-Ink builds
|
||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
fsi.positions.nodelist_lastheard = numframes;
|
fsi.positions.nodelist_bearings = numframes;
|
||||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen;
|
normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen;
|
||||||
indicatorIcons.push_back(icon_nodes);
|
indicatorIcons.push_back(icon_nodes);
|
||||||
|
|
||||||
@ -916,11 +916,15 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
fsi.positions.nodelist_distance = numframes;
|
fsi.positions.nodelist_distance = numframes;
|
||||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen;
|
normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen;
|
||||||
indicatorIcons.push_back(icon_distance);
|
indicatorIcons.push_back(icon_distance);
|
||||||
#endif
|
|
||||||
#if HAS_GPS
|
|
||||||
fsi.positions.nodelist_bearings = numframes;
|
fsi.positions.nodelist_bearings = numframes;
|
||||||
normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
|
normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses;
|
||||||
indicatorIcons.push_back(icon_list);
|
indicatorIcons.push_back(icon_list);
|
||||||
|
#endif
|
||||||
|
#if HAS_GPS
|
||||||
|
fsi.positions.nodelist_brc = numframes;
|
||||||
|
normalFrames[numframes++] = graphics::NodeListRenderer::drawBRCList;
|
||||||
|
indicatorIcons.push_back(icon_bm);
|
||||||
|
|
||||||
fsi.positions.gps = numframes;
|
fsi.positions.gps = numframes;
|
||||||
normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen;
|
normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen;
|
||||||
@ -1386,7 +1390,8 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
|
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
|
||||||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_distance ||
|
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_distance ||
|
||||||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
|
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
|
||||||
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_bearings) {
|
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_bearings ||
|
||||||
|
this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_brc) {
|
||||||
menuHandler::nodeListMenu();
|
menuHandler::nodeListMenu();
|
||||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.wifi) {
|
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.wifi) {
|
||||||
menuHandler::wifiBaseMenu();
|
menuHandler::wifiBaseMenu();
|
||||||
@ -1447,4 +1452,4 @@ bool shouldWakeOnReceivedMessage()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -662,6 +662,7 @@ class Screen : public concurrency::OSThread
|
|||||||
uint8_t nodelist_hopsignal = 255;
|
uint8_t nodelist_hopsignal = 255;
|
||||||
uint8_t nodelist_distance = 255;
|
uint8_t nodelist_distance = 255;
|
||||||
uint8_t nodelist_bearings = 255;
|
uint8_t nodelist_bearings = 255;
|
||||||
|
uint8_t nodelist_brc = 255;
|
||||||
uint8_t clock = 255;
|
uint8_t clock = 255;
|
||||||
uint8_t firstFavorite = 255;
|
uint8_t firstFavorite = 255;
|
||||||
uint8_t lastFavorite = 255;
|
uint8_t lastFavorite = 255;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "UIRenderer.h"
|
#include "UIRenderer.h"
|
||||||
#include "gps/GeoCoord.h"
|
#include "gps/GeoCoord.h"
|
||||||
#include "gps/RTC.h" // for getTime() function
|
#include "gps/RTC.h" // for getTime() function
|
||||||
|
#include "graphics/BRC.h"
|
||||||
#include "graphics/ScreenFonts.h"
|
#include "graphics/ScreenFonts.h"
|
||||||
#include "graphics/SharedUIDisplay.h"
|
#include "graphics/SharedUIDisplay.h"
|
||||||
#include "graphics/images.h"
|
#include "graphics/images.h"
|
||||||
@ -87,6 +88,8 @@ const char *getCurrentModeTitle(int screenWidth)
|
|||||||
#endif
|
#endif
|
||||||
case MODE_DISTANCE:
|
case MODE_DISTANCE:
|
||||||
return "Distance";
|
return "Distance";
|
||||||
|
case MODE_BEARING:
|
||||||
|
return "Bearing";
|
||||||
default:
|
default:
|
||||||
return "Nodes";
|
return "Nodes";
|
||||||
}
|
}
|
||||||
@ -309,6 +312,9 @@ void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
|||||||
case MODE_DISTANCE:
|
case MODE_DISTANCE:
|
||||||
drawNodeDistance(display, node, x, y, columnWidth);
|
drawNodeDistance(display, node, x, y, columnWidth);
|
||||||
break;
|
break;
|
||||||
|
case MODE_BEARING:
|
||||||
|
drawEntryCompass(display, node, x, y, columnWidth);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -335,6 +341,35 @@ void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawEntryBRC(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth)
|
||||||
|
{
|
||||||
|
bool isLeftCol = (x < SCREEN_WIDTH / 2);
|
||||||
|
|
||||||
|
// Adjust max text width depending on column and screen width
|
||||||
|
int nameMaxWidth = columnWidth - (isHighResolution ? (isLeftCol ? 25 : 28) : (isLeftCol ? 20 : 22));
|
||||||
|
|
||||||
|
const char *nodeName = getSafeNodeName(node);
|
||||||
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
|
display->setFont(FONT_SMALL);
|
||||||
|
auto xText = x + ((isHighResolution) ? 6 : 3);
|
||||||
|
display->drawString(xText, y, nodeName);
|
||||||
|
|
||||||
|
if (nodeDB->hasValidPosition(node)) {
|
||||||
|
char buf[14] = "";
|
||||||
|
BRCAddress(node->position.latitude_i, node->position.longitude_i).compact(buf, 14);
|
||||||
|
auto nameWidth = display->getStringWidth("WWWW") - 2; // Fixed width so they are aligned.
|
||||||
|
display->drawString(xText + nameWidth, y, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->is_favorite) {
|
||||||
|
if (isHighResolution) {
|
||||||
|
drawScaledXBitmap16x16(x, y + 6, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint, display);
|
||||||
|
} else {
|
||||||
|
display->drawXbm(x, y + 5, smallbulletpoint_width, smallbulletpoint_height, smallbulletpoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading,
|
void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth, float myHeading,
|
||||||
double userLat, double userLon)
|
double userLat, double userLon)
|
||||||
{
|
{
|
||||||
@ -384,17 +419,46 @@ void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawLastSeenExtra(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth,
|
||||||
|
float /*myHeading*/, double /*userLat*/, double /*userLon*/)
|
||||||
|
{
|
||||||
|
char timeStr[10];
|
||||||
|
uint32_t seconds = sinceLastSeen(node);
|
||||||
|
if (seconds == 0 || seconds == UINT32_MAX) {
|
||||||
|
snprintf(timeStr, sizeof(timeStr), "?");
|
||||||
|
} else {
|
||||||
|
uint32_t minutes = seconds / 60, hours = minutes / 60, days = hours / 24;
|
||||||
|
snprintf(timeStr, sizeof(timeStr), (days > 99 ? "?" : "%d%c"),
|
||||||
|
(days ? days
|
||||||
|
: hours ? hours
|
||||||
|
: minutes),
|
||||||
|
(days ? 'd'
|
||||||
|
: hours ? 'h'
|
||||||
|
: 'm'));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLeftCol = (x < SCREEN_WIDTH / 2);
|
||||||
|
int timeOffset = (isHighResolution) ? (isLeftCol ? 7 : 10) : (isLeftCol ? 3 : 7);
|
||||||
|
int rightEdge = x + columnWidth - timeOffset;
|
||||||
|
if (timeStr[strlen(timeStr) - 1] == 'm') // Fix the fact that our fonts don't line up well all the time
|
||||||
|
rightEdge -= 1;
|
||||||
|
// display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||||
|
int textWidth = display->getStringWidth(timeStr);
|
||||||
|
display->drawString(rightEdge - textWidth, y, timeStr);
|
||||||
|
}
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// Main Screen Functions
|
// Main Screen Functions
|
||||||
// =============================
|
// =============================
|
||||||
|
|
||||||
void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title,
|
void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title,
|
||||||
EntryRenderer renderer, NodeExtrasRenderer extras, float heading, double lat, double lon)
|
EntryRenderer renderer, NodeExtrasRenderer extras, float heading, double lat, double lon,
|
||||||
|
int totalColumns)
|
||||||
{
|
{
|
||||||
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
|
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
|
||||||
const int rowYOffset = FONT_HEIGHT_SMALL - 3;
|
const int rowYOffset = FONT_HEIGHT_SMALL - 3;
|
||||||
|
|
||||||
int columnWidth = display->getWidth() / 2;
|
int columnWidth = display->getWidth() / totalColumns;
|
||||||
|
|
||||||
display->clear();
|
display->clear();
|
||||||
|
|
||||||
@ -408,7 +472,6 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
|
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
|
||||||
|
|
||||||
int visibleNodeRows = totalRowsAvailable;
|
int visibleNodeRows = totalRowsAvailable;
|
||||||
int totalColumns = 2;
|
|
||||||
|
|
||||||
int startIndex = scrollIndex * visibleNodeRows * totalColumns;
|
int startIndex = scrollIndex * visibleNodeRows * totalColumns;
|
||||||
if (nodeDB->getMeshNodeByIndex(startIndex)->num == nodeDB->getNodeNum()) {
|
if (nodeDB->getMeshNodeByIndex(startIndex)->num == nodeDB->getNodeNum()) {
|
||||||
@ -446,7 +509,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw column separator
|
// Draw column separator
|
||||||
if (shownCount > 0) {
|
if (shownCount > 0 && totalColumns > 1) {
|
||||||
const int firstNodeY = y + 3;
|
const int firstNodeY = y + 3;
|
||||||
drawColumnSeparator(display, x, firstNodeY, lastNodeY);
|
drawColumnSeparator(display, x, firstNodeY, lastNodeY);
|
||||||
}
|
}
|
||||||
@ -482,7 +545,31 @@ void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state,
|
|||||||
|
|
||||||
// Render screen based on currentMode
|
// Render screen based on currentMode
|
||||||
const char *title = getCurrentModeTitle(display->getWidth());
|
const char *title = getCurrentModeTitle(display->getWidth());
|
||||||
drawNodeListScreen(display, state, x, y, title, drawEntryDynamic);
|
|
||||||
|
if (currentMode == MODE_BEARING) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING) {
|
||||||
|
if (screen->hasHeading()) {
|
||||||
|
heading = screen->getHeading(); // degrees
|
||||||
|
validHeading = true;
|
||||||
|
} else {
|
||||||
|
heading = screen->estimatedHeading(lat, lon);
|
||||||
|
validHeading = !isnan(heading);
|
||||||
|
}
|
||||||
|
if (!validHeading) {
|
||||||
|
lastRenderedMode = MODE_COUNT;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawNodeListScreen(display, state, x, y, title, drawEntryCompass, drawCompassArrow, heading, lat, lon);
|
||||||
|
} else {
|
||||||
|
drawNodeListScreen(display, state, x, y, title, drawEntryDynamic);
|
||||||
|
}
|
||||||
|
|
||||||
// Track the last mode to avoid reinitializing modeStartTime
|
// Track the last mode to avoid reinitializing modeStartTime
|
||||||
lastRenderedMode = currentMode;
|
lastRenderedMode = currentMode;
|
||||||
@ -539,6 +626,11 @@ void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state,
|
|||||||
drawNodeListScreen(display, state, x, y, "Bearings", drawEntryCompass, drawCompassArrow, heading, lat, lon);
|
drawNodeListScreen(display, state, x, y, "Bearings", drawEntryCompass, drawCompassArrow, heading, lat, lon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawBRCList(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
|
{
|
||||||
|
drawNodeListScreen(display, state, x, y, "BRC", drawEntryBRC, drawLastSeenExtra, 0, 0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw a series of fields in a column, wrapping to multiple columns if needed
|
/// Draw a series of fields in a column, wrapping to multiple columns if needed
|
||||||
void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
||||||
{
|
{
|
||||||
@ -564,4 +656,4 @@ void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields
|
|||||||
|
|
||||||
} // namespace NodeListRenderer
|
} // namespace NodeListRenderer
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
#endif
|
#endif
|
||||||
|
@ -24,12 +24,12 @@ typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t,
|
|||||||
typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int, float, double, double);
|
typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int, float, double, double);
|
||||||
|
|
||||||
// Node list mode enumeration
|
// Node list mode enumeration
|
||||||
enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_COUNT = 3 };
|
enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_BEARING = 3, MODE_COUNT = 4 };
|
||||||
|
|
||||||
// Main node list screen function
|
// Main node list screen function
|
||||||
void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title,
|
void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title,
|
||||||
EntryRenderer renderer, NodeExtrasRenderer extras = nullptr, float heading = 0, double lat = 0,
|
EntryRenderer renderer, NodeExtrasRenderer extras = nullptr, float heading = 0, double lat = 0,
|
||||||
double lon = 0);
|
double lon = 0, int totalColumns = 2);
|
||||||
|
|
||||||
// Entry renderers
|
// Entry renderers
|
||||||
void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
|
void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth);
|
||||||
@ -48,6 +48,7 @@ void drawHopSignalScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
void drawDistanceScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
void drawDistanceScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
void drawBRCList(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
// Utility functions
|
// Utility functions
|
||||||
const char *getCurrentModeTitle(int screenWidth);
|
const char *getCurrentModeTitle(int screenWidth);
|
||||||
|
@ -156,6 +156,18 @@ const uint8_t icon_list[] PROGMEM = {
|
|||||||
0x82 // Row 7: #.....#.
|
0x82 // Row 7: #.....#.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ➤ The Man Icon (8x8)
|
||||||
|
const uint8_t icon_bm[] PROGMEM = {
|
||||||
|
0x42, // Row 0: .#....#.
|
||||||
|
0x3C, // Row 1: ..####..
|
||||||
|
0x3C, // Row 2: ..####..
|
||||||
|
0x18, // Row 3: ...##...
|
||||||
|
0x18, // Row 4: ...##...
|
||||||
|
0x24, // Row 5: ..#..#..
|
||||||
|
0x24, // Row 6: ..#..#..
|
||||||
|
0x42 // Row 7: .#....#.
|
||||||
|
};
|
||||||
|
|
||||||
// 📶 Signal Bars Icon (left to right, small to large with spacing)
|
// 📶 Signal Bars Icon (left to right, small to large with spacing)
|
||||||
const uint8_t icon_signal[] PROGMEM = {
|
const uint8_t icon_signal[] PROGMEM = {
|
||||||
0b00000000, // ░░░░░░░
|
0b00000000, // ░░░░░░░
|
||||||
|
Loading…
Reference in New Issue
Block a user