mirror of
https://github.com/meshtastic/firmware.git
synced 2025-04-28 02:32:09 +00:00

* Move waypoint screen draw into the waypoint module * Get the observer set up for the waypoint screen draw * Static squashing: screen dimensions Macros moved back to Screen.cpp, as a band-aid until we eventually move all those static functions into the Screen class. * Move getCompassDiam into Screen class (supress compiler warnings) At this stage, the method is still static, because it's used by drawNodeInfo, which has no tidy reference to our screen instance. This is probably just another band-aid until these static functions all move. * Use new getCompassDiam function in AccelerometerThread * Properly gate display code in WaypointModule --------- Co-authored-by: Todd Herbert <herbert.todd@gmail.com>
170 lines
6.6 KiB
C++
170 lines
6.6 KiB
C++
#include "WaypointModule.h"
|
|
#include "NodeDB.h"
|
|
#include "PowerFSM.h"
|
|
#include "configuration.h"
|
|
#if HAS_SCREEN
|
|
#include "gps/RTC.h"
|
|
#include "graphics/Screen.h"
|
|
#include "main.h"
|
|
#endif
|
|
|
|
WaypointModule *waypointModule;
|
|
|
|
ProcessMessage WaypointModule::handleReceived(const meshtastic_MeshPacket &mp)
|
|
{
|
|
#ifdef DEBUG_PORT
|
|
auto &p = mp.decoded;
|
|
LOG_INFO("Received waypoint msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
|
|
#endif
|
|
UIFrameEvent e = {true, true};
|
|
// We only store/display messages destined for us.
|
|
// Keep a copy of the most recent text message.
|
|
devicestate.rx_waypoint = mp;
|
|
devicestate.has_rx_waypoint = true;
|
|
|
|
powerFSM.trigger(EVENT_RECEIVED_MSG);
|
|
notifyObservers(&e);
|
|
|
|
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
|
|
}
|
|
|
|
#if HAS_SCREEN
|
|
bool WaypointModule::shouldDraw()
|
|
{
|
|
#if !MESHTASTIC_EXCLUDE_WAYPOINT
|
|
// If no waypoint to show
|
|
if (!devicestate.has_rx_waypoint)
|
|
return false;
|
|
|
|
// Decode the message, to find the expiration time (is waypoint still valid)
|
|
// This handles "deletion" as well as expiration
|
|
meshtastic_Waypoint wp;
|
|
memset(&wp, 0, sizeof(wp));
|
|
if (pb_decode_from_bytes(devicestate.rx_waypoint.decoded.payload.bytes, devicestate.rx_waypoint.decoded.payload.size,
|
|
&meshtastic_Waypoint_msg, &wp)) {
|
|
// Valid waypoint
|
|
if (wp.expire > getTime())
|
|
return devicestate.has_rx_waypoint = true;
|
|
|
|
// Expired, or deleted
|
|
else
|
|
return devicestate.has_rx_waypoint = false;
|
|
}
|
|
|
|
// If decoding failed
|
|
LOG_ERROR("Failed to decode waypoint\n");
|
|
devicestate.has_rx_waypoint = false;
|
|
return false;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
/// Draw the last waypoint we received
|
|
void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
|
{
|
|
// Prepare to draw
|
|
display->setFont(FONT_SMALL);
|
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
|
|
// Handle inverted display
|
|
// Unsure of expected behavior: for now, copy drawNodeInfo
|
|
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED)
|
|
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
|
|
|
|
// Decode the waypoint
|
|
meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
|
|
meshtastic_Waypoint wp;
|
|
memset(&wp, 0, sizeof(wp));
|
|
if (!pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &wp)) {
|
|
// This *should* be caught by shouldDrawWaypoint, but we'll short-circuit here just in case
|
|
display->drawStringMaxWidth(0 + x, 0 + y, x + display->getWidth(), "Couldn't decode waypoint");
|
|
devicestate.has_rx_waypoint = false;
|
|
return;
|
|
}
|
|
|
|
// Get timestamp info. Will pass as a field to drawColumns
|
|
static char lastStr[20];
|
|
screen->getTimeAgoStr(sinceReceived(&mp), lastStr, sizeof(lastStr));
|
|
|
|
// Will contain distance information, passed as a field to drawColumns
|
|
static char distStr[20];
|
|
|
|
// Get our node, to use our own position
|
|
meshtastic_NodeInfoLite *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
|
|
const char *fields[] = {"Waypoint", lastStr, wp.name, distStr, NULL};
|
|
|
|
// Dimensions / co-ordinates for the compass/circle
|
|
int16_t compassX = 0, compassY = 0;
|
|
uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight());
|
|
|
|
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
|
compassX = x + display->getWidth() - compassDiam / 2 - 5;
|
|
compassY = y + display->getHeight() / 2;
|
|
} else {
|
|
compassX = x + display->getWidth() - compassDiam / 2 - 5;
|
|
compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2;
|
|
}
|
|
|
|
// If our node has a position:
|
|
if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) {
|
|
const meshtastic_PositionLite &op = ourNode->position;
|
|
float myHeading;
|
|
if (screen->hasHeading())
|
|
myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
|
|
else
|
|
myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
|
screen->drawCompassNorth(display, compassX, compassY, myHeading);
|
|
|
|
// Distance to Waypoint
|
|
float d = GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
|
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
|
if (d < (2 * MILES_TO_FEET))
|
|
snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET);
|
|
else
|
|
snprintf(distStr, sizeof(distStr), "%.1f mi", d * METERS_TO_FEET / MILES_TO_FEET);
|
|
} else {
|
|
if (d < 2000)
|
|
snprintf(distStr, sizeof(distStr), "%.0f m", d);
|
|
else
|
|
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
|
|
}
|
|
|
|
// Compass bearing to waypoint
|
|
float bearingToOther =
|
|
GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(wp.latitude_i), DegD(wp.longitude_i));
|
|
// If the top of the compass is a static north then bearingToOther can be drawn on the compass directly
|
|
// If the top of the compass is not a static north we need adjust bearingToOther based on heading
|
|
if (!config.display.compass_north_top)
|
|
bearingToOther -= myHeading;
|
|
screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther);
|
|
}
|
|
|
|
// If our node doesn't have position
|
|
else {
|
|
// ? in the compass
|
|
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
|
|
|
|
// ? in the distance field
|
|
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
|
|
strncpy(distStr, "? mi", sizeof(distStr));
|
|
else
|
|
strncpy(distStr, "? km", sizeof(distStr));
|
|
}
|
|
|
|
// Undo color-inversion, if set prior to drawing header
|
|
// Unsure of expected behavior? For now: copy drawNodeInfo
|
|
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
|
display->setColor(BLACK);
|
|
}
|
|
|
|
// Draw compass circle
|
|
display->drawCircle(compassX, compassY, compassDiam / 2);
|
|
|
|
// Must be after distStr is populated
|
|
screen->drawColumns(display, x, y, fields);
|
|
}
|
|
#endif |