mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-20 12:02:21 +00:00
Merge branch 'master' into tft-gui-work
This commit is contained in:
commit
9dc93078e1
@ -1 +1 @@
|
|||||||
Subproject commit 57ddb288e87438db3b5b99aa61f66a354c47bffb
|
Subproject commit e7327e76bdc0b3b77c50e214fae5beb1cb303e9c
|
@ -278,16 +278,17 @@ class AccelerometerThread : public concurrency::OSThread
|
|||||||
display->setFont(FONT_MEDIUM);
|
display->setFont(FONT_MEDIUM);
|
||||||
display->drawString(x, y, "Calibrating\nCompass");
|
display->drawString(x, y, "Calibrating\nCompass");
|
||||||
int16_t compassX = 0, compassY = 0;
|
int16_t compassX = 0, compassY = 0;
|
||||||
|
uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight());
|
||||||
|
|
||||||
// coordinates for the center of the compass/circle
|
// coordinates for the center of the compass/circle
|
||||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
||||||
compassX = x + display->getWidth() - getCompassDiam(display) / 2 - 5;
|
compassX = x + display->getWidth() - compassDiam / 2 - 5;
|
||||||
compassY = y + display->getHeight() / 2;
|
compassY = y + display->getHeight() / 2;
|
||||||
} else {
|
} else {
|
||||||
compassX = x + display->getWidth() - getCompassDiam(display) / 2 - 5;
|
compassX = x + display->getWidth() - compassDiam / 2 - 5;
|
||||||
compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2;
|
compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2;
|
||||||
}
|
}
|
||||||
display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
|
display->drawCircle(compassX, compassY, compassDiam / 2);
|
||||||
screen->drawCompassNorth(display, compassX, compassY, screen->getHeading() * PI / 180);
|
screen->drawCompassNorth(display, compassX, compassY, screen->getHeading() * PI / 180);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -43,7 +43,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "meshUtils.h"
|
#include "meshUtils.h"
|
||||||
#include "modules/ExternalNotificationModule.h"
|
#include "modules/ExternalNotificationModule.h"
|
||||||
#include "modules/TextMessageModule.h"
|
#include "modules/TextMessageModule.h"
|
||||||
#include "modules/WaypointModule.h"
|
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
|
|
||||||
@ -60,9 +59,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#include "platform/portduino/PortduinoGlue.h"
|
#include "platform/portduino/PortduinoGlue.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Convert an integer GPS coords to a floating point
|
|
||||||
#define DegD(i) (i * 1e-7)
|
|
||||||
|
|
||||||
using namespace meshtastic; /** @todo remove */
|
using namespace meshtastic; /** @todo remove */
|
||||||
|
|
||||||
namespace graphics
|
namespace graphics
|
||||||
@ -111,10 +107,10 @@ GeoCoord geoCoord;
|
|||||||
static bool heartbeat = false;
|
static bool heartbeat = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static uint16_t displayWidth, displayHeight;
|
// Quick access to screen dimensions from static drawing functions
|
||||||
|
// DEPRECATED. To-do: move static functions inside Screen class
|
||||||
#define SCREEN_WIDTH displayWidth
|
#define SCREEN_WIDTH display->getWidth()
|
||||||
#define SCREEN_HEIGHT displayHeight
|
#define SCREEN_HEIGHT display->getHeight()
|
||||||
|
|
||||||
#include "graphics/ScreenFonts.h"
|
#include "graphics/ScreenFonts.h"
|
||||||
|
|
||||||
@ -416,37 +412,6 @@ static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
|
|||||||
return packet->from != 0 && !moduleConfig.store_forward.enabled;
|
return packet->from != 0 && !moduleConfig.store_forward.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine whether the waypoint frame should be drawn (waypoint deleted? expired?)
|
|
||||||
static bool shouldDrawWaypoint(const meshtastic_MeshPacket *packet)
|
|
||||||
{
|
|
||||||
#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(packet->decoded.payload.bytes, packet->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 power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
|
// Draw power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
|
||||||
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
|
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
|
||||||
{
|
{
|
||||||
@ -1093,7 +1058,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
void Screen::drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
||||||
{
|
{
|
||||||
// The coordinates define the left starting point of the text
|
// The coordinates define the left starting point of the text
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
@ -1279,7 +1244,7 @@ static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const
|
|||||||
* We keep a series of "after you've gone 10 meters, what is your heading since
|
* We keep a series of "after you've gone 10 meters, what is your heading since
|
||||||
* the last reference point?"
|
* the last reference point?"
|
||||||
*/
|
*/
|
||||||
static float estimatedHeading(double lat, double lon)
|
float Screen::estimatedHeading(double lat, double lon)
|
||||||
{
|
{
|
||||||
static double oldLat, oldLon;
|
static double oldLat, oldLon;
|
||||||
static float b;
|
static float b;
|
||||||
@ -1309,7 +1274,7 @@ static size_t nodeIndex;
|
|||||||
static int8_t prevFrame = -1;
|
static int8_t prevFrame = -1;
|
||||||
|
|
||||||
// Draw the arrow pointing to a node's location
|
// Draw the arrow pointing to a node's location
|
||||||
static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
|
void Screen::drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian)
|
||||||
{
|
{
|
||||||
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
||||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||||
@ -1319,7 +1284,7 @@ static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t comp
|
|||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
arrowPoints[i]->rotate(headingRadian);
|
arrowPoints[i]->rotate(headingRadian);
|
||||||
arrowPoints[i]->scale(getCompassDiam(display) * 0.6);
|
arrowPoints[i]->scale(compassDiam * 0.6);
|
||||||
arrowPoints[i]->translate(compassX, compassY);
|
arrowPoints[i]->translate(compassX, compassY);
|
||||||
}
|
}
|
||||||
display->drawLine(tip.x, tip.y, tail.x, tail.y);
|
display->drawLine(tip.x, tip.y, tail.x, tail.y);
|
||||||
@ -1328,7 +1293,7 @@ static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t comp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get a string representation of the time passed since something happened
|
// Get a string representation of the time passed since something happened
|
||||||
static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
|
void Screen::getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
|
||||||
{
|
{
|
||||||
// Use an absolute timestamp in some cases.
|
// Use an absolute timestamp in some cases.
|
||||||
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
|
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
|
||||||
@ -1357,6 +1322,54 @@ static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
|
|||||||
snprintf(timeStr, maxLength, "unknown age");
|
snprintf(timeStr, maxLength, "unknown age");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Screen::drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
|
||||||
|
{
|
||||||
|
// If north is supposed to be at the top of the compass we want rotation to be +0
|
||||||
|
if (config.display.compass_north_top)
|
||||||
|
myHeading = -0;
|
||||||
|
|
||||||
|
Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f);
|
||||||
|
Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f);
|
||||||
|
Point *rosePoints[] = {&N1, &N2, &N3, &N4};
|
||||||
|
|
||||||
|
uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
// North on compass will be negative of heading
|
||||||
|
rosePoints[i]->rotate(-myHeading);
|
||||||
|
rosePoints[i]->scale(compassDiam);
|
||||||
|
rosePoints[i]->translate(compassX, compassY);
|
||||||
|
}
|
||||||
|
display->drawLine(N1.x, N1.y, N3.x, N3.y);
|
||||||
|
display->drawLine(N2.x, N2.y, N4.x, N4.y);
|
||||||
|
display->drawLine(N1.x, N1.y, N4.x, N4.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Screen::getCompassDiam(uint32_t displayWidth, uint32_t displayHeight)
|
||||||
|
{
|
||||||
|
uint16_t diam = 0;
|
||||||
|
uint16_t offset = 0;
|
||||||
|
|
||||||
|
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT)
|
||||||
|
offset = FONT_HEIGHT_SMALL;
|
||||||
|
|
||||||
|
// get the smaller of the 2 dimensions and subtract 20
|
||||||
|
if (displayWidth > (displayHeight - offset)) {
|
||||||
|
diam = displayHeight - offset;
|
||||||
|
// if 2/3 of the other size would be smaller, use that
|
||||||
|
if (diam > (displayWidth * 2 / 3)) {
|
||||||
|
diam = displayWidth * 2 / 3;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
diam = displayWidth;
|
||||||
|
if (diam > ((displayHeight - offset) * 2 / 3)) {
|
||||||
|
diam = (displayHeight - offset) * 2 / 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return diam - 20;
|
||||||
|
};
|
||||||
|
|
||||||
static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
// We only advance our nodeIndex if the frame # has changed - because
|
// We only advance our nodeIndex if the frame # has changed - because
|
||||||
@ -1396,7 +1409,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
}
|
}
|
||||||
|
|
||||||
static char lastStr[20];
|
static char lastStr[20];
|
||||||
getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
|
screen->getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
|
||||||
|
|
||||||
static char distStr[20];
|
static char distStr[20];
|
||||||
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
||||||
@ -1407,13 +1420,14 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||||
const char *fields[] = {username, lastStr, signalStr, distStr, NULL};
|
const char *fields[] = {username, lastStr, signalStr, distStr, NULL};
|
||||||
int16_t compassX = 0, compassY = 0;
|
int16_t compassX = 0, compassY = 0;
|
||||||
|
uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
|
|
||||||
// coordinates for the center of the compass/circle
|
// coordinates for the center of the compass/circle
|
||||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
||||||
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
|
compassX = x + SCREEN_WIDTH - compassDiam / 2 - 5;
|
||||||
compassY = y + SCREEN_HEIGHT / 2;
|
compassY = y + SCREEN_HEIGHT / 2;
|
||||||
} else {
|
} else {
|
||||||
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
|
compassX = x + SCREEN_WIDTH - compassDiam / 2 - 5;
|
||||||
compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2;
|
compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2;
|
||||||
}
|
}
|
||||||
bool hasNodeHeading = false;
|
bool hasNodeHeading = false;
|
||||||
@ -1424,7 +1438,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
if (screen->hasHeading())
|
if (screen->hasHeading())
|
||||||
myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
|
myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
|
||||||
else
|
else
|
||||||
myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||||
screen->drawCompassNorth(display, compassX, compassY, myHeading);
|
screen->drawCompassNorth(display, compassX, compassY, myHeading);
|
||||||
|
|
||||||
if (hasValidPosition(node)) {
|
if (hasValidPosition(node)) {
|
||||||
@ -1452,7 +1466,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
// If the top of the compass is not a static north we need adjust bearingToOther based on heading
|
// If the top of the compass is not a static north we need adjust bearingToOther based on heading
|
||||||
if (!config.display.compass_north_top)
|
if (!config.display.compass_north_top)
|
||||||
bearingToOther -= myHeading;
|
bearingToOther -= myHeading;
|
||||||
drawNodeHeading(display, compassX, compassY, bearingToOther);
|
screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasNodeHeading) {
|
if (!hasNodeHeading) {
|
||||||
@ -1462,119 +1476,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
// hasValidPosition(node));
|
// hasValidPosition(node));
|
||||||
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
|
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
|
||||||
}
|
}
|
||||||
display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
|
display->drawCircle(compassX, compassY, compassDiam / 2);
|
||||||
|
|
||||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
||||||
display->setColor(BLACK);
|
display->setColor(BLACK);
|
||||||
}
|
}
|
||||||
// Must be after distStr is populated
|
// Must be after distStr is populated
|
||||||
drawColumns(display, x, y, fields);
|
screen->drawColumns(display, x, y, fields);
|
||||||
}
|
|
||||||
|
|
||||||
/// Draw the last waypoint we received
|
|
||||||
static void drawWaypointFrame(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];
|
|
||||||
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};
|
|
||||||
|
|
||||||
// Co-ordinates for the center of the compass/circle
|
|
||||||
int16_t compassX = 0, compassY = 0;
|
|
||||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
|
||||||
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
|
|
||||||
compassY = y + SCREEN_HEIGHT / 2;
|
|
||||||
} else {
|
|
||||||
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
|
|
||||||
compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - 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 = 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;
|
|
||||||
drawNodeHeading(display, compassX, compassY, 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, getCompassDiam(display) / 2);
|
|
||||||
|
|
||||||
// Must be after distStr is populated
|
|
||||||
drawColumns(display, x, y, fields);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
|
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
|
||||||
@ -1797,8 +1705,6 @@ void Screen::setup()
|
|||||||
textMessageObserver.observe(textMessageModule);
|
textMessageObserver.observe(textMessageModule);
|
||||||
if (inputBroker)
|
if (inputBroker)
|
||||||
inputObserver.observe(inputBroker);
|
inputObserver.observe(inputBroker);
|
||||||
if (waypointModule)
|
|
||||||
waypointObserver.observe(waypointModule);
|
|
||||||
|
|
||||||
// Modules can notify screen about refresh
|
// Modules can notify screen about refresh
|
||||||
MeshModule::observeUIEvents(&uiFrameEventObserver);
|
MeshModule::observeUIEvents(&uiFrameEventObserver);
|
||||||
@ -2130,11 +2036,6 @@ void Screen::setFrames()
|
|||||||
normalFrames[numframes++] = drawTextMessageFrame;
|
normalFrames[numframes++] = drawTextMessageFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have a waypoint (not expired, not deleted)
|
|
||||||
if (devicestate.has_rx_waypoint && shouldDrawWaypoint(&devicestate.rx_waypoint)) {
|
|
||||||
normalFrames[numframes++] = drawWaypointFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
// then all the nodes
|
// then all the nodes
|
||||||
// We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
|
// We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
|
||||||
size_t numToShow = min(numMeshNodes, 4U);
|
size_t numToShow = min(numMeshNodes, 4U);
|
||||||
@ -2196,7 +2097,7 @@ void Screen::blink()
|
|||||||
uint8_t count = 10;
|
uint8_t count = 10;
|
||||||
dispdev->setBrightness(254);
|
dispdev->setBrightness(254);
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
dispdev->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
dispdev->fillRect(0, 0, dispdev->getWidth(), dispdev->getHeight());
|
||||||
dispdev->display();
|
dispdev->display();
|
||||||
delay(50);
|
delay(50);
|
||||||
dispdev->clear();
|
dispdev->clear();
|
||||||
@ -2687,13 +2588,6 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Screen::handleWaypoint(const meshtastic_MeshPacket *arg)
|
|
||||||
{
|
|
||||||
// TODO: move to appropriate frame when redrawing
|
|
||||||
setFrames();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
#else
|
#else
|
||||||
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
|
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
|
||||||
|
@ -86,6 +86,9 @@ class Screen
|
|||||||
#define SEGMENT_WIDTH 16
|
#define SEGMENT_WIDTH 16
|
||||||
#define SEGMENT_HEIGHT 4
|
#define SEGMENT_HEIGHT 4
|
||||||
|
|
||||||
|
/// Convert an integer GPS coords to a floating point
|
||||||
|
#define DegD(i) (i * 1e-7)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
/// A basic 2D point class for drawing
|
/// A basic 2D point class for drawing
|
||||||
@ -123,31 +126,6 @@ class Point
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
static uint16_t getCompassDiam(OLEDDisplay *display)
|
|
||||||
{
|
|
||||||
uint16_t diam = 0;
|
|
||||||
uint16_t offset = 0;
|
|
||||||
|
|
||||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT)
|
|
||||||
offset = FONT_HEIGHT_SMALL;
|
|
||||||
|
|
||||||
// get the smaller of the 2 dimensions and subtract 20
|
|
||||||
if (display->getWidth() > (display->getHeight() - offset)) {
|
|
||||||
diam = display->getHeight() - offset;
|
|
||||||
// if 2/3 of the other size would be smaller, use that
|
|
||||||
if (diam > (display->getWidth() * 2 / 3)) {
|
|
||||||
diam = display->getWidth() * 2 / 3;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
diam = display->getWidth();
|
|
||||||
if (diam > ((display->getHeight() - offset) * 2 / 3)) {
|
|
||||||
diam = (display->getHeight() - offset) * 2 / 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return diam - 20;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -192,8 +170,6 @@ class Screen : public concurrency::OSThread
|
|||||||
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||||
CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
|
CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
|
||||||
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
|
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
|
||||||
CallbackObserver<Screen, const meshtastic_MeshPacket *> waypointObserver =
|
|
||||||
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleWaypoint);
|
|
||||||
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
|
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
|
||||||
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent);
|
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent);
|
||||||
CallbackObserver<Screen, const InputEvent *> inputObserver =
|
CallbackObserver<Screen, const InputEvent *> inputObserver =
|
||||||
@ -236,27 +212,18 @@ class Screen : public concurrency::OSThread
|
|||||||
|
|
||||||
void drawFrameText(OLEDDisplay *, OLEDDisplayUiState *, int16_t, int16_t, const char *);
|
void drawFrameText(OLEDDisplay *, OLEDDisplayUiState *, int16_t, int16_t, const char *);
|
||||||
|
|
||||||
|
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
|
||||||
|
|
||||||
// Draw north
|
// Draw north
|
||||||
void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
|
void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading);
|
||||||
{
|
|
||||||
// If north is supposed to be at the top of the compass we want rotation to be +0
|
|
||||||
if (config.display.compass_north_top)
|
|
||||||
myHeading = -0;
|
|
||||||
|
|
||||||
Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f);
|
static uint16_t getCompassDiam(uint32_t displayWidth, uint32_t displayHeight);
|
||||||
Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f);
|
|
||||||
Point *rosePoints[] = {&N1, &N2, &N3, &N4};
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
float estimatedHeading(double lat, double lon);
|
||||||
// North on compass will be negative of heading
|
|
||||||
rosePoints[i]->rotate(-myHeading);
|
void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian);
|
||||||
rosePoints[i]->scale(getCompassDiam(display));
|
|
||||||
rosePoints[i]->translate(compassX, compassY);
|
void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields);
|
||||||
}
|
|
||||||
display->drawLine(N1.x, N1.y, N3.x, N3.y);
|
|
||||||
display->drawLine(N2.x, N2.y, N4.x, N4.y);
|
|
||||||
display->drawLine(N1.x, N1.y, N4.x, N4.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle button press, trackball or swipe action)
|
/// Handle button press, trackball or swipe action)
|
||||||
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
||||||
@ -425,7 +392,6 @@ class Screen : public concurrency::OSThread
|
|||||||
int handleTextMessage(const meshtastic_MeshPacket *arg);
|
int handleTextMessage(const meshtastic_MeshPacket *arg);
|
||||||
int handleUIFrameEvent(const UIFrameEvent *arg);
|
int handleUIFrameEvent(const UIFrameEvent *arg);
|
||||||
int handleInputEvent(const InputEvent *arg);
|
int handleInputEvent(const InputEvent *arg);
|
||||||
int handleWaypoint(const meshtastic_MeshPacket *arg);
|
|
||||||
|
|
||||||
/// Used to force (super slow) eink displays to draw critical frames
|
/// Used to force (super slow) eink displays to draw critical frames
|
||||||
void forceDisplay(bool forceUiUpdate = false);
|
void forceDisplay(bool forceUiUpdate = false);
|
||||||
@ -448,6 +414,11 @@ class Screen : public concurrency::OSThread
|
|||||||
|
|
||||||
bool isAUTOOled = false;
|
bool isAUTOOled = false;
|
||||||
|
|
||||||
|
// Screen dimensions (for convenience)
|
||||||
|
// Defined during Screen::setup
|
||||||
|
uint16_t displayWidth = 0;
|
||||||
|
uint16_t displayHeight = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FrameCallback alertFrames[1];
|
FrameCallback alertFrames[1];
|
||||||
struct ScreenCmd {
|
struct ScreenCmd {
|
||||||
|
@ -22,7 +22,6 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
|||||||
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
|
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
|
||||||
printPacket("Ignoring incoming msg we've already seen", p);
|
printPacket("Ignoring incoming msg we've already seen", p);
|
||||||
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
||||||
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT &&
|
|
||||||
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||||
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
|
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
|
||||||
Router::cancelSending(p->from, p->id);
|
Router::cancelSending(p->from, p->id);
|
||||||
|
@ -261,7 +261,6 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
|
|||||||
uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax);
|
uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax);
|
||||||
// LOG_DEBUG("rx_snr of %f so setting CWsize to:%d\n", snr, CWsize);
|
// LOG_DEBUG("rx_snr of %f so setting CWsize to:%d\n", snr, CWsize);
|
||||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||||
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT ||
|
|
||||||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||||
delay = random(0, 2 * CWsize) * slotTimeMsec;
|
delay = random(0, 2 * CWsize) * slotTimeMsec;
|
||||||
LOG_DEBUG("rx_snr found in packet. As a router, setting tx delay:%d\n", delay);
|
LOG_DEBUG("rx_snr found in packet. As a router, setting tx delay:%d\n", delay);
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#include "meshtastic/channel.pb.h"
|
#include "meshtastic/channel.pb.h"
|
||||||
#include "meshtastic/localonly.pb.h"
|
#include "meshtastic/localonly.pb.h"
|
||||||
#include "meshtastic/mesh.pb.h"
|
#include "meshtastic/mesh.pb.h"
|
||||||
#include "meshtastic/module_config.pb.h"
|
|
||||||
#include "meshtastic/telemetry.pb.h"
|
#include "meshtastic/telemetry.pb.h"
|
||||||
|
|
||||||
#if PB_PROTO_HEADER_VERSION != 40
|
#if PB_PROTO_HEADER_VERSION != 40
|
||||||
|
@ -36,7 +36,7 @@ PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, AUTO)
|
|||||||
PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO)
|
PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO)
|
||||||
|
|
||||||
|
|
||||||
PB_BIND(meshtastic_LogRecord, meshtastic_LogRecord, AUTO)
|
PB_BIND(meshtastic_LogRecord, meshtastic_LogRecord, 2)
|
||||||
|
|
||||||
|
|
||||||
PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
|
PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
|
||||||
|
@ -691,11 +691,11 @@ typedef struct _meshtastic_MyNodeInfo {
|
|||||||
and then extend as needed by emitting multiple records. */
|
and then extend as needed by emitting multiple records. */
|
||||||
typedef struct _meshtastic_LogRecord {
|
typedef struct _meshtastic_LogRecord {
|
||||||
/* Log levels, chosen to match python logging conventions. */
|
/* Log levels, chosen to match python logging conventions. */
|
||||||
char message[64];
|
char message[384];
|
||||||
/* Seconds since 1970 - or 0 for unknown/unset */
|
/* Seconds since 1970 - or 0 for unknown/unset */
|
||||||
uint32_t time;
|
uint32_t time;
|
||||||
/* Usually based on thread name - if known */
|
/* Usually based on thread name - if known */
|
||||||
char source[8];
|
char source[32];
|
||||||
/* Not yet set */
|
/* Not yet set */
|
||||||
meshtastic_LogRecord_Level level;
|
meshtastic_LogRecord_Level level;
|
||||||
} meshtastic_LogRecord;
|
} meshtastic_LogRecord;
|
||||||
@ -1507,7 +1507,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
|||||||
#define meshtastic_FileInfo_size 236
|
#define meshtastic_FileInfo_size 236
|
||||||
#define meshtastic_FromRadio_size 510
|
#define meshtastic_FromRadio_size 510
|
||||||
#define meshtastic_Heartbeat_size 0
|
#define meshtastic_Heartbeat_size 0
|
||||||
#define meshtastic_LogRecord_size 81
|
#define meshtastic_LogRecord_size 426
|
||||||
#define meshtastic_MeshPacket_size 326
|
#define meshtastic_MeshPacket_size 326
|
||||||
#define meshtastic_MqttClientProxyMessage_size 501
|
#define meshtastic_MqttClientProxyMessage_size 501
|
||||||
#define meshtastic_MyNodeInfo_size 18
|
#define meshtastic_MyNodeInfo_size 18
|
||||||
|
@ -124,6 +124,8 @@ typedef enum _meshtastic_PortNum {
|
|||||||
meshtastic_PortNum_ATAK_PLUGIN = 72,
|
meshtastic_PortNum_ATAK_PLUGIN = 72,
|
||||||
/* Provides unencrypted information about a node for consumption by a map via MQTT */
|
/* Provides unencrypted information about a node for consumption by a map via MQTT */
|
||||||
meshtastic_PortNum_MAP_REPORT_APP = 73,
|
meshtastic_PortNum_MAP_REPORT_APP = 73,
|
||||||
|
/* PowerStress based monitoring support (for automated power consumption testing) */
|
||||||
|
meshtastic_PortNum_POWERSTRESS_APP = 74,
|
||||||
/* Private applications should use portnums >= 256.
|
/* Private applications should use portnums >= 256.
|
||||||
To simplify initial development and testing you can use "PRIVATE_APP"
|
To simplify initial development and testing you can use "PRIVATE_APP"
|
||||||
in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */
|
in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */
|
||||||
|
@ -9,5 +9,9 @@
|
|||||||
PB_BIND(meshtastic_PowerMon, meshtastic_PowerMon, AUTO)
|
PB_BIND(meshtastic_PowerMon, meshtastic_PowerMon, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
PB_BIND(meshtastic_PowerStressMessage, meshtastic_PowerStressMessage, AUTO)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -38,6 +38,33 @@ See GPSPowerState for more details */
|
|||||||
meshtastic_PowerMon_State_GPS_Active = 2048
|
meshtastic_PowerMon_State_GPS_Active = 2048
|
||||||
} meshtastic_PowerMon_State;
|
} meshtastic_PowerMon_State;
|
||||||
|
|
||||||
|
/* What operation would we like the UUT to perform.
|
||||||
|
note: senders should probably set want_response in their request packets, so that they can know when the state
|
||||||
|
machine has started processing their request */
|
||||||
|
typedef enum _meshtastic_PowerStressMessage_Opcode {
|
||||||
|
/* Unset/unused */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_UNSET = 0,
|
||||||
|
meshtastic_PowerStressMessage_Opcode_PRINT_INFO = 1, /* Print board version slog and send an ack that we are alive and ready to process commands */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_FORCE_QUIET = 2, /* Try to turn off all automatic processing of packets, screen, sleeping, etc (to make it easier to measure in isolation) */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_END_QUIET = 3, /* Stop powerstress processing - probably by just rebooting the board */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_SCREEN_ON = 16, /* Turn the screen on */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_SCREEN_OFF = 17, /* Turn the screen off */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_CPU_IDLE = 32, /* Let the CPU run but we assume mostly idling for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_CPU_DEEPSLEEP = 33, /* Force deep sleep for FIXME seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_CPU_FULLON = 34, /* Spin the CPU as fast as possible for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_LED_ON = 48, /* Turn the LED on for num_seconds (and leave it on - for baseline power measurement purposes) */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_LED_OFF = 49, /* Force the LED off for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_LORA_OFF = 64, /* Completely turn off the LORA radio for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_LORA_TX = 65, /* Send Lora packets for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_LORA_RX = 66, /* Receive Lora packets for num_seconds (node will be mostly just listening, unless an external agent is helping stress this by sending packets on the current channel) */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_BT_OFF = 80, /* Turn off the BT radio for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_BT_ON = 81, /* Turn on the BT radio for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_WIFI_OFF = 96, /* Turn off the WIFI radio for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_WIFI_ON = 97, /* Turn on the WIFI radio for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_GPS_OFF = 112, /* Turn off the GPS radio for num_seconds */
|
||||||
|
meshtastic_PowerStressMessage_Opcode_GPS_ON = 113 /* Turn on the GPS radio for num_seconds */
|
||||||
|
} meshtastic_PowerStressMessage_Opcode;
|
||||||
|
|
||||||
/* Struct definitions */
|
/* Struct definitions */
|
||||||
/* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
|
/* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
|
||||||
But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
|
But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
|
||||||
@ -45,6 +72,13 @@ typedef struct _meshtastic_PowerMon {
|
|||||||
char dummy_field;
|
char dummy_field;
|
||||||
} meshtastic_PowerMon;
|
} meshtastic_PowerMon;
|
||||||
|
|
||||||
|
/* PowerStress testing support via the C++ PowerStress module */
|
||||||
|
typedef struct _meshtastic_PowerStressMessage {
|
||||||
|
/* What type of HardwareMessage is this? */
|
||||||
|
meshtastic_PowerStressMessage_Opcode cmd;
|
||||||
|
float num_seconds;
|
||||||
|
} meshtastic_PowerStressMessage;
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -55,13 +89,23 @@ extern "C" {
|
|||||||
#define _meshtastic_PowerMon_State_MAX meshtastic_PowerMon_State_GPS_Active
|
#define _meshtastic_PowerMon_State_MAX meshtastic_PowerMon_State_GPS_Active
|
||||||
#define _meshtastic_PowerMon_State_ARRAYSIZE ((meshtastic_PowerMon_State)(meshtastic_PowerMon_State_GPS_Active+1))
|
#define _meshtastic_PowerMon_State_ARRAYSIZE ((meshtastic_PowerMon_State)(meshtastic_PowerMon_State_GPS_Active+1))
|
||||||
|
|
||||||
|
#define _meshtastic_PowerStressMessage_Opcode_MIN meshtastic_PowerStressMessage_Opcode_UNSET
|
||||||
|
#define _meshtastic_PowerStressMessage_Opcode_MAX meshtastic_PowerStressMessage_Opcode_GPS_ON
|
||||||
|
#define _meshtastic_PowerStressMessage_Opcode_ARRAYSIZE ((meshtastic_PowerStressMessage_Opcode)(meshtastic_PowerStressMessage_Opcode_GPS_ON+1))
|
||||||
|
|
||||||
|
|
||||||
|
#define meshtastic_PowerStressMessage_cmd_ENUMTYPE meshtastic_PowerStressMessage_Opcode
|
||||||
|
|
||||||
|
|
||||||
/* Initializer values for message structs */
|
/* Initializer values for message structs */
|
||||||
#define meshtastic_PowerMon_init_default {0}
|
#define meshtastic_PowerMon_init_default {0}
|
||||||
|
#define meshtastic_PowerStressMessage_init_default {_meshtastic_PowerStressMessage_Opcode_MIN, 0}
|
||||||
#define meshtastic_PowerMon_init_zero {0}
|
#define meshtastic_PowerMon_init_zero {0}
|
||||||
|
#define meshtastic_PowerStressMessage_init_zero {_meshtastic_PowerStressMessage_Opcode_MIN, 0}
|
||||||
|
|
||||||
/* Field tags (for use in manual encoding/decoding) */
|
/* Field tags (for use in manual encoding/decoding) */
|
||||||
|
#define meshtastic_PowerStressMessage_cmd_tag 1
|
||||||
|
#define meshtastic_PowerStressMessage_num_seconds_tag 2
|
||||||
|
|
||||||
/* Struct field encoding specification for nanopb */
|
/* Struct field encoding specification for nanopb */
|
||||||
#define meshtastic_PowerMon_FIELDLIST(X, a) \
|
#define meshtastic_PowerMon_FIELDLIST(X, a) \
|
||||||
@ -69,14 +113,23 @@ extern "C" {
|
|||||||
#define meshtastic_PowerMon_CALLBACK NULL
|
#define meshtastic_PowerMon_CALLBACK NULL
|
||||||
#define meshtastic_PowerMon_DEFAULT NULL
|
#define meshtastic_PowerMon_DEFAULT NULL
|
||||||
|
|
||||||
|
#define meshtastic_PowerStressMessage_FIELDLIST(X, a) \
|
||||||
|
X(a, STATIC, SINGULAR, UENUM, cmd, 1) \
|
||||||
|
X(a, STATIC, SINGULAR, FLOAT, num_seconds, 2)
|
||||||
|
#define meshtastic_PowerStressMessage_CALLBACK NULL
|
||||||
|
#define meshtastic_PowerStressMessage_DEFAULT NULL
|
||||||
|
|
||||||
extern const pb_msgdesc_t meshtastic_PowerMon_msg;
|
extern const pb_msgdesc_t meshtastic_PowerMon_msg;
|
||||||
|
extern const pb_msgdesc_t meshtastic_PowerStressMessage_msg;
|
||||||
|
|
||||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||||
#define meshtastic_PowerMon_fields &meshtastic_PowerMon_msg
|
#define meshtastic_PowerMon_fields &meshtastic_PowerMon_msg
|
||||||
|
#define meshtastic_PowerStressMessage_fields &meshtastic_PowerStressMessage_msg
|
||||||
|
|
||||||
/* Maximum encoded size of messages (where known) */
|
/* Maximum encoded size of messages (where known) */
|
||||||
#define MESHTASTIC_MESHTASTIC_POWERMON_PB_H_MAX_SIZE meshtastic_PowerMon_size
|
#define MESHTASTIC_MESHTASTIC_POWERMON_PB_H_MAX_SIZE meshtastic_PowerStressMessage_size
|
||||||
#define meshtastic_PowerMon_size 0
|
#define meshtastic_PowerMon_size 0
|
||||||
|
#define meshtastic_PowerStressMessage_size 7
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
@ -388,6 +388,10 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
|||||||
LOG_DEBUG("Tried to set node_info_broadcast_secs too low, setting to %d\n", min_node_info_broadcast_secs);
|
LOG_DEBUG("Tried to set node_info_broadcast_secs too low, setting to %d\n", min_node_info_broadcast_secs);
|
||||||
config.device.node_info_broadcast_secs = min_node_info_broadcast_secs;
|
config.device.node_info_broadcast_secs = min_node_info_broadcast_secs;
|
||||||
}
|
}
|
||||||
|
// Router Client is deprecated; Set it to client
|
||||||
|
if (c.payload_variant.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT) {
|
||||||
|
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case meshtastic_Config_position_tag:
|
case meshtastic_Config_position_tag:
|
||||||
LOG_INFO("Setting config: Position\n");
|
LOG_INFO("Setting config: Position\n");
|
||||||
|
@ -2,6 +2,11 @@
|
|||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "PowerFSM.h"
|
#include "PowerFSM.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
|
#if HAS_SCREEN
|
||||||
|
#include "gps/RTC.h"
|
||||||
|
#include "graphics/Screen.h"
|
||||||
|
#include "main.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
WaypointModule *waypointModule;
|
WaypointModule *waypointModule;
|
||||||
|
|
||||||
@ -11,14 +16,155 @@ ProcessMessage WaypointModule::handleReceived(const meshtastic_MeshPacket &mp)
|
|||||||
auto &p = mp.decoded;
|
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);
|
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
|
#endif
|
||||||
|
UIFrameEvent e = {true, true};
|
||||||
// We only store/display messages destined for us.
|
// We only store/display messages destined for us.
|
||||||
// Keep a copy of the most recent text message.
|
// Keep a copy of the most recent text message.
|
||||||
devicestate.rx_waypoint = mp;
|
devicestate.rx_waypoint = mp;
|
||||||
devicestate.has_rx_waypoint = true;
|
devicestate.has_rx_waypoint = true;
|
||||||
|
|
||||||
powerFSM.trigger(EVENT_RECEIVED_MSG);
|
powerFSM.trigger(EVENT_RECEIVED_MSG);
|
||||||
notifyObservers(&mp);
|
notifyObservers(&e);
|
||||||
|
|
||||||
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
|
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
|
@ -5,20 +5,28 @@
|
|||||||
/**
|
/**
|
||||||
* Waypoint message handling for meshtastic
|
* Waypoint message handling for meshtastic
|
||||||
*/
|
*/
|
||||||
class WaypointModule : public SinglePortModule, public Observable<const meshtastic_MeshPacket *>
|
class WaypointModule : public SinglePortModule, public Observable<const UIFrameEvent *>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/** Constructor
|
/** Constructor
|
||||||
* name is for debugging output
|
* name is for debugging output
|
||||||
*/
|
*/
|
||||||
WaypointModule() : SinglePortModule("waypoint", meshtastic_PortNum_WAYPOINT_APP) {}
|
WaypointModule() : SinglePortModule("waypoint", meshtastic_PortNum_WAYPOINT_APP) {}
|
||||||
|
#if HAS_SCREEN
|
||||||
|
bool shouldDraw();
|
||||||
|
#endif
|
||||||
protected:
|
protected:
|
||||||
/** Called to handle a particular incoming message
|
/** Called to handle a particular incoming message
|
||||||
|
|
||||||
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
|
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
|
||||||
it
|
it
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
virtual Observable<const UIFrameEvent *> *getUIFrameObservable() override { return this; }
|
||||||
|
#if HAS_SCREEN
|
||||||
|
virtual bool wantUIFrame() override { return this->shouldDraw(); }
|
||||||
|
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
|
||||||
|
#endif
|
||||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -319,8 +319,8 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m
|
|||||||
#ifdef ARCH_ESP32
|
#ifdef ARCH_ESP32
|
||||||
if (moduleConfig.store_forward.enabled) {
|
if (moduleConfig.store_forward.enabled) {
|
||||||
|
|
||||||
// The router node should not be sending messages as a client. Unless he is a ROUTER_CLIENT
|
// The router node should not be sending messages as a client
|
||||||
if ((getFrom(&mp) != nodeDB->getNodeNum()) || (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT)) {
|
if ((getFrom(&mp) != nodeDB->getNodeNum())) {
|
||||||
|
|
||||||
if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) {
|
if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) {
|
||||||
auto &p = mp.decoded;
|
auto &p = mp.decoded;
|
||||||
|
Loading…
Reference in New Issue
Block a user