mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-16 18:12:07 +00:00
Merge branch 'master' into tft-gui-work
This commit is contained in:
commit
a29497e2a3
@ -44,7 +44,7 @@ lib_deps =
|
|||||||
${networking_base.lib_deps}
|
${networking_base.lib_deps}
|
||||||
${environmental_base.lib_deps}
|
${environmental_base.lib_deps}
|
||||||
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
|
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
|
||||||
h2zero/NimBLE-Arduino@^1.4.1
|
h2zero/NimBLE-Arduino@^1.4.2
|
||||||
https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587
|
https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587
|
||||||
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
|
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
|
||||||
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
|
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
|
||||||
|
@ -169,7 +169,7 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
|||||||
|
|
||||||
isContinuationMessage = !hasNewline;
|
isContinuationMessage = !hasNewline;
|
||||||
|
|
||||||
if (config.bluetooth.device_logging_enabled) {
|
if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) {
|
||||||
bool isBleConnected = false;
|
bool isBleConnected = false;
|
||||||
#ifdef ARCH_ESP32
|
#ifdef ARCH_ESP32
|
||||||
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
|
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
|
||||||
|
@ -43,6 +43,7 @@ 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"
|
||||||
|
|
||||||
@ -59,6 +60,9 @@ 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
|
||||||
@ -446,6 +450,37 @@ 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)
|
||||||
{
|
{
|
||||||
@ -1091,43 +1126,6 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the last waypoint we received
|
|
||||||
static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
|
||||||
{
|
|
||||||
static char tempBuf[237];
|
|
||||||
|
|
||||||
meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
|
|
||||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
|
|
||||||
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
||||||
display->setFont(FONT_SMALL);
|
|
||||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
|
||||||
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
|
|
||||||
display->setColor(BLACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t seconds = sinceReceived(&mp);
|
|
||||||
uint32_t minutes = seconds / 60;
|
|
||||||
uint32_t hours = minutes / 60;
|
|
||||||
uint32_t days = hours / 24;
|
|
||||||
|
|
||||||
if (config.display.heading_bold) {
|
|
||||||
display->drawStringf(1 + x, 0 + y, tempBuf, "%s ago from %s",
|
|
||||||
screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
|
||||||
(node && node->has_user) ? node->user.short_name : "???");
|
|
||||||
}
|
|
||||||
display->drawStringf(0 + x, 0 + y, tempBuf, "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
|
||||||
(node && node->has_user) ? node->user.short_name : "???");
|
|
||||||
|
|
||||||
display->setColor(WHITE);
|
|
||||||
meshtastic_Waypoint scratch;
|
|
||||||
memset(&scratch, 0, sizeof(scratch));
|
|
||||||
if (pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
|
|
||||||
snprintf(tempBuf, sizeof(tempBuf), "Received waypoint: %s", scratch.name);
|
|
||||||
display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)
|
static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
||||||
{
|
{
|
||||||
@ -1453,8 +1451,35 @@ static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t com
|
|||||||
drawLine(display, N1, N4);
|
drawLine(display, N1, N4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert an integer GPS coords to a floating point
|
// Get a string representation of the time passed since something happened
|
||||||
#define DegD(i) (i * 1e-7)
|
static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
|
||||||
|
{
|
||||||
|
// Use an absolute timestamp in some cases.
|
||||||
|
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
|
||||||
|
uint8_t timestampHours, timestampMinutes;
|
||||||
|
int32_t daysAgo;
|
||||||
|
bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo);
|
||||||
|
|
||||||
|
if (agoSecs < 120) // last 2 mins?
|
||||||
|
snprintf(timeStr, maxLength, "%u seconds ago", agoSecs);
|
||||||
|
// -- if suitable for timestamp --
|
||||||
|
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
|
||||||
|
snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
|
||||||
|
else if (useTimestamp && daysAgo == 0) // Today
|
||||||
|
snprintf(timeStr, maxLength, "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
|
||||||
|
else if (useTimestamp && daysAgo == 1) // Yesterday
|
||||||
|
snprintf(timeStr, maxLength, "Seen yesterday");
|
||||||
|
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
|
||||||
|
snprintf(timeStr, maxLength, "%li days ago", (long)daysAgo);
|
||||||
|
// -- if using time delta instead --
|
||||||
|
else if (agoSecs < 120 * 60) // last 2 hrs
|
||||||
|
snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / 60);
|
||||||
|
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
|
||||||
|
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
|
||||||
|
snprintf(timeStr, maxLength, "%u hours ago", agoSecs / 60 / 60);
|
||||||
|
else
|
||||||
|
snprintf(timeStr, maxLength, "unknown age");
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
@ -1494,34 +1519,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
|
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t agoSecs = sinceLastSeen(node);
|
|
||||||
static char lastStr[20];
|
static char lastStr[20];
|
||||||
|
getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
|
||||||
// Use an absolute timestamp in some cases.
|
|
||||||
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
|
|
||||||
uint8_t timestampHours, timestampMinutes;
|
|
||||||
int32_t daysAgo;
|
|
||||||
bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo);
|
|
||||||
|
|
||||||
if (agoSecs < 120) // last 2 mins?
|
|
||||||
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
|
|
||||||
// -- if suitable for timestamp --
|
|
||||||
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
|
|
||||||
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
|
|
||||||
else if (useTimestamp && daysAgo == 0) // Today
|
|
||||||
snprintf(lastStr, sizeof(lastStr), "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
|
|
||||||
else if (useTimestamp && daysAgo == 1) // Yesterday
|
|
||||||
snprintf(lastStr, sizeof(lastStr), "Seen yesterday");
|
|
||||||
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
|
|
||||||
snprintf(lastStr, sizeof(lastStr), "%li days ago", (long)daysAgo);
|
|
||||||
// -- if using time delta instead --
|
|
||||||
else if (agoSecs < 120 * 60) // last 2 hrs
|
|
||||||
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
|
|
||||||
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
|
|
||||||
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
|
|
||||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
|
||||||
else
|
|
||||||
snprintf(lastStr, sizeof(lastStr), "unknown age");
|
|
||||||
|
|
||||||
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) {
|
||||||
@ -1596,6 +1595,112 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
drawColumns(display, x, y, fields);
|
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));
|
||||||
|
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)
|
||||||
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
|
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
|
||||||
{
|
{
|
||||||
@ -1806,6 +1911,8 @@ 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);
|
||||||
@ -2133,8 +2240,9 @@ void Screen::setFrames()
|
|||||||
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
|
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
|
||||||
normalFrames[numframes++] = drawTextMessageFrame;
|
normalFrames[numframes++] = drawTextMessageFrame;
|
||||||
}
|
}
|
||||||
// If we have a waypoint - show it next, unless it's a phone message and we aren't using any special modules
|
|
||||||
if (devicestate.has_rx_waypoint && shouldDrawMessage(&devicestate.rx_waypoint)) {
|
// If we have a waypoint (not expired, not deleted)
|
||||||
|
if (devicestate.has_rx_waypoint && shouldDrawWaypoint(&devicestate.rx_waypoint)) {
|
||||||
normalFrames[numframes++] = drawWaypointFrame;
|
normalFrames[numframes++] = drawWaypointFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2736,6 +2844,13 @@ 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) {}
|
||||||
|
@ -130,6 +130,8 @@ 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 =
|
||||||
@ -340,6 +342,7 @@ 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);
|
||||||
|
@ -167,6 +167,7 @@ bool isVibrating = false;
|
|||||||
bool eink_found = true;
|
bool eink_found = true;
|
||||||
|
|
||||||
uint32_t serialSinceMsec;
|
uint32_t serialSinceMsec;
|
||||||
|
bool pauseBluetoothLogging = false;
|
||||||
|
|
||||||
bool pmu_found;
|
bool pmu_found;
|
||||||
|
|
||||||
|
@ -85,6 +85,8 @@ extern uint32_t serialSinceMsec;
|
|||||||
// This will suppress the current delay and instead try to run ASAP.
|
// This will suppress the current delay and instead try to run ASAP.
|
||||||
extern bool runASAP;
|
extern bool runASAP;
|
||||||
|
|
||||||
|
extern bool pauseBluetoothLogging;
|
||||||
|
|
||||||
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds(), enterDfuMode();
|
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds(), enterDfuMode();
|
||||||
|
|
||||||
meshtastic_DeviceMetadata getDeviceMetadata();
|
meshtastic_DeviceMetadata getDeviceMetadata();
|
||||||
|
@ -46,6 +46,7 @@ void PhoneAPI::handleStartConfig()
|
|||||||
|
|
||||||
// even if we were already connected - restart our state machine
|
// even if we were already connected - restart our state machine
|
||||||
state = STATE_SEND_MY_INFO;
|
state = STATE_SEND_MY_INFO;
|
||||||
|
pauseBluetoothLogging = true;
|
||||||
|
|
||||||
LOG_INFO("Starting API client config\n");
|
LOG_INFO("Starting API client config\n");
|
||||||
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
|
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
|
||||||
@ -352,9 +353,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
fromRadioScratch.config_complete_id = config_nonce;
|
fromRadioScratch.config_complete_id = config_nonce;
|
||||||
config_nonce = 0;
|
config_nonce = 0;
|
||||||
state = STATE_SEND_PACKETS;
|
state = STATE_SEND_PACKETS;
|
||||||
|
pauseBluetoothLogging = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_SEND_PACKETS:
|
case STATE_SEND_PACKETS:
|
||||||
|
pauseBluetoothLogging = false;
|
||||||
// Do we have a message from the mesh or packet from the local device?
|
// Do we have a message from the mesh or packet from the local device?
|
||||||
LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n");
|
LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n");
|
||||||
if (queueStatusPacketForPhone) {
|
if (queueStatusPacketForPhone) {
|
||||||
@ -398,6 +401,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
|
|
||||||
void PhoneAPI::handleDisconnect()
|
void PhoneAPI::handleDisconnect()
|
||||||
{
|
{
|
||||||
|
pauseBluetoothLogging = false;
|
||||||
LOG_INFO("PhoneAPI disconnect\n");
|
LOG_INFO("PhoneAPI disconnect\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,8 +98,6 @@ class PhoneAPI
|
|||||||
|
|
||||||
bool isConnected() { return state != STATE_SEND_NOTHING; }
|
bool isConnected() { return state != STATE_SEND_NOTHING; }
|
||||||
|
|
||||||
void setInitialState() { state = STATE_SEND_MY_INFO; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Our fromradio packet while it is being assembled
|
/// Our fromradio packet while it is being assembled
|
||||||
meshtastic_FromRadio fromRadioScratch = {};
|
meshtastic_FromRadio fromRadioScratch = {};
|
||||||
|
@ -85,53 +85,90 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
|
|||||||
return false; // Let others look at this message also if they want
|
return false; // Let others look at this message also if they want
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
|
||||||
{
|
{
|
||||||
if (!aqi.read(&data)) {
|
if (!aqi.read(&data)) {
|
||||||
LOG_WARN("Skipping send measurements. Could not read AQIn\n");
|
LOG_WARN("Skipping send measurements. Could not read AQIn\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
m->time = getTime();
|
||||||
m.time = getTime();
|
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
|
||||||
m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
|
m->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
|
||||||
m.variant.air_quality_metrics.pm10_standard = data.pm10_standard;
|
m->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
|
||||||
m.variant.air_quality_metrics.pm25_standard = data.pm25_standard;
|
m->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
|
||||||
m.variant.air_quality_metrics.pm100_standard = data.pm100_standard;
|
|
||||||
|
|
||||||
m.variant.air_quality_metrics.pm10_environmental = data.pm10_env;
|
m->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
|
||||||
m.variant.air_quality_metrics.pm25_environmental = data.pm25_env;
|
m->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
|
||||||
m.variant.air_quality_metrics.pm100_environmental = data.pm100_env;
|
m->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
|
||||||
|
|
||||||
LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i\n",
|
LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i\n",
|
||||||
m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard,
|
m->variant.air_quality_metrics.pm10_standard, m->variant.air_quality_metrics.pm25_standard,
|
||||||
m.variant.air_quality_metrics.pm100_standard);
|
m->variant.air_quality_metrics.pm100_standard);
|
||||||
|
|
||||||
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n",
|
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n",
|
||||||
m.variant.air_quality_metrics.pm10_environmental, m.variant.air_quality_metrics.pm25_environmental,
|
m->variant.air_quality_metrics.pm10_environmental, m->variant.air_quality_metrics.pm25_environmental,
|
||||||
m.variant.air_quality_metrics.pm100_environmental);
|
m->variant.air_quality_metrics.pm100_environmental);
|
||||||
|
|
||||||
meshtastic_MeshPacket *p = allocDataProtobuf(m);
|
|
||||||
p->to = dest;
|
|
||||||
p->decoded.want_response = false;
|
|
||||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
|
|
||||||
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
|
|
||||||
else
|
|
||||||
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
|
||||||
|
|
||||||
// release previous packet before occupying a new spot
|
|
||||||
if (lastMeasurementPacket != nullptr)
|
|
||||||
packetPool.release(lastMeasurementPacket);
|
|
||||||
|
|
||||||
lastMeasurementPacket = packetPool.allocCopy(*p);
|
|
||||||
if (phoneOnly) {
|
|
||||||
LOG_INFO("Sending packet to phone\n");
|
|
||||||
service.sendToPhone(p);
|
|
||||||
} else {
|
|
||||||
LOG_INFO("Sending packet to mesh\n");
|
|
||||||
service.sendToMesh(p, RX_SRC_LOCAL, true);
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply()
|
||||||
|
{
|
||||||
|
if (currentRequest) {
|
||||||
|
auto req = *currentRequest;
|
||||||
|
const auto &p = req.decoded;
|
||||||
|
meshtastic_Telemetry scratch;
|
||||||
|
meshtastic_Telemetry *decoded = NULL;
|
||||||
|
memset(&scratch, 0, sizeof(scratch));
|
||||||
|
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||||
|
decoded = &scratch;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Error decoding AirQualityTelemetry module!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// Check for a request for air quality metrics
|
||||||
|
if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
|
||||||
|
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||||
|
if (getAirQualityTelemetry(&m)) {
|
||||||
|
LOG_INFO("Air quality telemetry replying to request\n");
|
||||||
|
return allocDataProtobuf(m);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||||
|
{
|
||||||
|
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||||
|
if (getAirQualityTelemetry(&m)) {
|
||||||
|
meshtastic_MeshPacket *p = allocDataProtobuf(m);
|
||||||
|
p->to = dest;
|
||||||
|
p->decoded.want_response = false;
|
||||||
|
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
|
||||||
|
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
|
||||||
|
else
|
||||||
|
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
|
||||||
|
|
||||||
|
// release previous packet before occupying a new spot
|
||||||
|
if (lastMeasurementPacket != nullptr)
|
||||||
|
packetPool.release(lastMeasurementPacket);
|
||||||
|
|
||||||
|
lastMeasurementPacket = packetPool.allocCopy(*p);
|
||||||
|
if (phoneOnly) {
|
||||||
|
LOG_INFO("Sending packet to phone\n");
|
||||||
|
service.sendToPhone(p);
|
||||||
|
} else {
|
||||||
|
LOG_INFO("Sending packet to mesh\n");
|
||||||
|
service.sendToMesh(p, RX_SRC_LOCAL, true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -26,6 +26,11 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
|
|||||||
*/
|
*/
|
||||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
|
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
|
||||||
virtual int32_t runOnce() override;
|
virtual int32_t runOnce() override;
|
||||||
|
/** Called to get current Air Quality data
|
||||||
|
@return true if it contains valid data
|
||||||
|
*/
|
||||||
|
bool getAirQualityTelemetry(meshtastic_Telemetry *m);
|
||||||
|
virtual meshtastic_MeshPacket *allocReply() override;
|
||||||
/**
|
/**
|
||||||
* Send our Telemetry into the mesh
|
* Send our Telemetry into the mesh
|
||||||
*/
|
*/
|
||||||
|
@ -52,14 +52,27 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
|
|||||||
|
|
||||||
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
|
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
|
||||||
{
|
{
|
||||||
if (ignoreRequest) {
|
if (currentRequest) {
|
||||||
return NULL;
|
auto req = *currentRequest;
|
||||||
|
const auto &p = req.decoded;
|
||||||
|
meshtastic_Telemetry scratch;
|
||||||
|
meshtastic_Telemetry *decoded = NULL;
|
||||||
|
memset(&scratch, 0, sizeof(scratch));
|
||||||
|
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||||
|
decoded = &scratch;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Error decoding DeviceTelemetry module!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// Check for a request for device metrics
|
||||||
|
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
|
||||||
|
LOG_INFO("Device telemetry replying to request\n");
|
||||||
|
|
||||||
|
meshtastic_Telemetry telemetry = getDeviceTelemetry();
|
||||||
|
return allocDataProtobuf(telemetry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return NULL;
|
||||||
LOG_INFO("Device telemetry replying to request\n");
|
|
||||||
|
|
||||||
meshtastic_Telemetry telemetry = getDeviceTelemetry();
|
|
||||||
return allocDataProtobuf(telemetry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
|
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
|
||||||
|
@ -270,98 +270,129 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
|
|||||||
return false; // Let others look at this message also if they want
|
return false; // Let others look at this message also if they want
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m)
|
||||||
{
|
{
|
||||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
bool hasSensor = false;
|
bool hasSensor = false;
|
||||||
m.time = getTime();
|
m->time = getTime();
|
||||||
m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
|
m->which_variant = meshtastic_Telemetry_environment_metrics_tag;
|
||||||
|
|
||||||
if (dfRobotLarkSensor.hasSensor()) {
|
if (dfRobotLarkSensor.hasSensor()) {
|
||||||
valid = valid && dfRobotLarkSensor.getMetrics(&m);
|
valid = valid && dfRobotLarkSensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (sht31Sensor.hasSensor()) {
|
if (sht31Sensor.hasSensor()) {
|
||||||
valid = valid && sht31Sensor.getMetrics(&m);
|
valid = valid && sht31Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (lps22hbSensor.hasSensor()) {
|
if (lps22hbSensor.hasSensor()) {
|
||||||
valid = valid && lps22hbSensor.getMetrics(&m);
|
valid = valid && lps22hbSensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (shtc3Sensor.hasSensor()) {
|
if (shtc3Sensor.hasSensor()) {
|
||||||
valid = valid && shtc3Sensor.getMetrics(&m);
|
valid = valid && shtc3Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (bmp085Sensor.hasSensor()) {
|
if (bmp085Sensor.hasSensor()) {
|
||||||
valid = valid && bmp085Sensor.getMetrics(&m);
|
valid = valid && bmp085Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (bmp280Sensor.hasSensor()) {
|
if (bmp280Sensor.hasSensor()) {
|
||||||
valid = valid && bmp280Sensor.getMetrics(&m);
|
valid = valid && bmp280Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (bme280Sensor.hasSensor()) {
|
if (bme280Sensor.hasSensor()) {
|
||||||
valid = valid && bme280Sensor.getMetrics(&m);
|
valid = valid && bme280Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (bme680Sensor.hasSensor()) {
|
if (bme680Sensor.hasSensor()) {
|
||||||
valid = valid && bme680Sensor.getMetrics(&m);
|
valid = valid && bme680Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (mcp9808Sensor.hasSensor()) {
|
if (mcp9808Sensor.hasSensor()) {
|
||||||
valid = valid && mcp9808Sensor.getMetrics(&m);
|
valid = valid && mcp9808Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (ina219Sensor.hasSensor()) {
|
if (ina219Sensor.hasSensor()) {
|
||||||
valid = valid && ina219Sensor.getMetrics(&m);
|
valid = valid && ina219Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (ina260Sensor.hasSensor()) {
|
if (ina260Sensor.hasSensor()) {
|
||||||
valid = valid && ina260Sensor.getMetrics(&m);
|
valid = valid && ina260Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (veml7700Sensor.hasSensor()) {
|
if (veml7700Sensor.hasSensor()) {
|
||||||
valid = valid && veml7700Sensor.getMetrics(&m);
|
valid = valid && veml7700Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (tsl2591Sensor.hasSensor()) {
|
if (tsl2591Sensor.hasSensor()) {
|
||||||
valid = valid && tsl2591Sensor.getMetrics(&m);
|
valid = valid && tsl2591Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (opt3001Sensor.hasSensor()) {
|
if (opt3001Sensor.hasSensor()) {
|
||||||
valid = valid && opt3001Sensor.getMetrics(&m);
|
valid = valid && opt3001Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (mlx90632Sensor.hasSensor()) {
|
if (mlx90632Sensor.hasSensor()) {
|
||||||
valid = valid && mlx90632Sensor.getMetrics(&m);
|
valid = valid && mlx90632Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (rcwl9620Sensor.hasSensor()) {
|
if (rcwl9620Sensor.hasSensor()) {
|
||||||
valid = valid && rcwl9620Sensor.getMetrics(&m);
|
valid = valid && rcwl9620Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (nau7802Sensor.hasSensor()) {
|
if (nau7802Sensor.hasSensor()) {
|
||||||
valid = valid && nau7802Sensor.getMetrics(&m);
|
valid = valid && nau7802Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
}
|
}
|
||||||
if (aht10Sensor.hasSensor()) {
|
if (aht10Sensor.hasSensor()) {
|
||||||
if (!bmp280Sensor.hasSensor()) {
|
if (!bmp280Sensor.hasSensor()) {
|
||||||
valid = valid && aht10Sensor.getMetrics(&m);
|
valid = valid && aht10Sensor.getMetrics(m);
|
||||||
hasSensor = true;
|
hasSensor = true;
|
||||||
} else {
|
} else {
|
||||||
// prefer bmp280 temp if both sensors are present, fetch only humidity
|
// prefer bmp280 temp if both sensors are present, fetch only humidity
|
||||||
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
|
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
|
||||||
LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n");
|
LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n");
|
||||||
aht10Sensor.getMetrics(&m_ahtx);
|
aht10Sensor.getMetrics(&m_ahtx);
|
||||||
m.variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
|
m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
valid = valid && hasSensor;
|
return valid && hasSensor;
|
||||||
|
}
|
||||||
|
|
||||||
if (valid) {
|
meshtastic_MeshPacket *EnvironmentTelemetryModule::allocReply()
|
||||||
|
{
|
||||||
|
if (currentRequest) {
|
||||||
|
auto req = *currentRequest;
|
||||||
|
const auto &p = req.decoded;
|
||||||
|
meshtastic_Telemetry scratch;
|
||||||
|
meshtastic_Telemetry *decoded = NULL;
|
||||||
|
memset(&scratch, 0, sizeof(scratch));
|
||||||
|
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||||
|
decoded = &scratch;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Error decoding EnvironmentTelemetry module!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// Check for a request for environment metrics
|
||||||
|
if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
|
||||||
|
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||||
|
if (getEnvironmentTelemetry(&m)) {
|
||||||
|
LOG_INFO("Environment telemetry replying to request\n");
|
||||||
|
return allocDataProtobuf(m);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||||
|
{
|
||||||
|
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||||
|
if (getEnvironmentTelemetry(&m)) {
|
||||||
LOG_INFO("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f\n",
|
LOG_INFO("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f\n",
|
||||||
m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current,
|
m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current,
|
||||||
m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity,
|
m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity,
|
||||||
@ -399,8 +430,9 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
|||||||
setIntervalFromNow(5000);
|
setIntervalFromNow(5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return valid;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
|
AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
|
||||||
|
@ -32,6 +32,11 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu
|
|||||||
*/
|
*/
|
||||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
|
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
|
||||||
virtual int32_t runOnce() override;
|
virtual int32_t runOnce() override;
|
||||||
|
/** Called to get current Environment telemetry data
|
||||||
|
@return true if it contains valid data
|
||||||
|
*/
|
||||||
|
bool getEnvironmentTelemetry(meshtastic_Telemetry *m);
|
||||||
|
virtual meshtastic_MeshPacket *allocReply() override;
|
||||||
/**
|
/**
|
||||||
* Send our Telemetry into the mesh
|
* Send our Telemetry into the mesh
|
||||||
*/
|
*/
|
||||||
|
@ -163,29 +163,63 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m
|
|||||||
return false; // Let others look at this message also if they want
|
return false; // Let others look at this message also if they want
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m)
|
||||||
|
{
|
||||||
|
bool valid = false;
|
||||||
|
m->time = getTime();
|
||||||
|
m->which_variant = meshtastic_Telemetry_power_metrics_tag;
|
||||||
|
|
||||||
|
m->variant.power_metrics.ch1_voltage = 0;
|
||||||
|
m->variant.power_metrics.ch1_current = 0;
|
||||||
|
m->variant.power_metrics.ch2_voltage = 0;
|
||||||
|
m->variant.power_metrics.ch2_current = 0;
|
||||||
|
m->variant.power_metrics.ch3_voltage = 0;
|
||||||
|
m->variant.power_metrics.ch3_current = 0;
|
||||||
|
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
|
||||||
|
if (ina219Sensor.hasSensor())
|
||||||
|
valid = ina219Sensor.getMetrics(m);
|
||||||
|
if (ina260Sensor.hasSensor())
|
||||||
|
valid = ina260Sensor.getMetrics(m);
|
||||||
|
if (ina3221Sensor.hasSensor())
|
||||||
|
valid = ina3221Sensor.getMetrics(m);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
meshtastic_MeshPacket *PowerTelemetryModule::allocReply()
|
||||||
|
{
|
||||||
|
if (currentRequest) {
|
||||||
|
auto req = *currentRequest;
|
||||||
|
const auto &p = req.decoded;
|
||||||
|
meshtastic_Telemetry scratch;
|
||||||
|
meshtastic_Telemetry *decoded = NULL;
|
||||||
|
memset(&scratch, 0, sizeof(scratch));
|
||||||
|
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||||
|
decoded = &scratch;
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Error decoding PowerTelemetry module!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// Check for a request for power metrics
|
||||||
|
if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
|
||||||
|
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||||
|
if (getPowerTelemetry(&m)) {
|
||||||
|
LOG_INFO("Power telemetry replying to request\n");
|
||||||
|
return allocDataProtobuf(m);
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||||
{
|
{
|
||||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||||
bool valid = false;
|
if (getPowerTelemetry(&m)) {
|
||||||
m.time = getTime();
|
|
||||||
m.which_variant = meshtastic_Telemetry_power_metrics_tag;
|
|
||||||
|
|
||||||
m.variant.power_metrics.ch1_voltage = 0;
|
|
||||||
m.variant.power_metrics.ch1_current = 0;
|
|
||||||
m.variant.power_metrics.ch2_voltage = 0;
|
|
||||||
m.variant.power_metrics.ch2_current = 0;
|
|
||||||
m.variant.power_metrics.ch3_voltage = 0;
|
|
||||||
m.variant.power_metrics.ch3_current = 0;
|
|
||||||
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
|
|
||||||
if (ina219Sensor.hasSensor())
|
|
||||||
valid = ina219Sensor.getMetrics(&m);
|
|
||||||
if (ina260Sensor.hasSensor())
|
|
||||||
valid = ina260Sensor.getMetrics(&m);
|
|
||||||
if (ina3221Sensor.hasSensor())
|
|
||||||
valid = ina3221Sensor.getMetrics(&m);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (valid) {
|
|
||||||
LOG_INFO("(Sending): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, "
|
LOG_INFO("(Sending): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, "
|
||||||
"ch3_voltage=%f, ch3_current=%f\n",
|
"ch3_voltage=%f, ch3_current=%f\n",
|
||||||
m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage,
|
m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage,
|
||||||
@ -218,8 +252,9 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
|||||||
setIntervalFromNow(5000);
|
setIntervalFromNow(5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return valid;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -33,6 +33,11 @@ class PowerTelemetryModule : private concurrency::OSThread, public ProtobufModul
|
|||||||
*/
|
*/
|
||||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
|
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
|
||||||
virtual int32_t runOnce() override;
|
virtual int32_t runOnce() override;
|
||||||
|
/** Called to get current Power telemetry data
|
||||||
|
@return true if it contains valid data
|
||||||
|
*/
|
||||||
|
bool getPowerTelemetry(meshtastic_Telemetry *m);
|
||||||
|
virtual meshtastic_MeshPacket *allocReply() override;
|
||||||
/**
|
/**
|
||||||
* Send our Telemetry into the mesh
|
* Send our Telemetry into the mesh
|
||||||
*/
|
*/
|
||||||
|
@ -16,8 +16,7 @@ int32_t INA3221Sensor::runOnce()
|
|||||||
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||||
}
|
}
|
||||||
if (!status) {
|
if (!status) {
|
||||||
ina3221.setAddr(INA3221_ADDR42_SDA); // i2c address 0x42
|
ina3221.begin(nodeTelemetrySensorsMap[sensorType].second);
|
||||||
ina3221.begin();
|
|
||||||
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
|
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
|
||||||
status = true;
|
status = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -66,10 +66,6 @@ bool PaxcounterModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, m
|
|||||||
|
|
||||||
meshtastic_MeshPacket *PaxcounterModule::allocReply()
|
meshtastic_MeshPacket *PaxcounterModule::allocReply()
|
||||||
{
|
{
|
||||||
if (ignoreRequest) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
meshtastic_Paxcount pl = meshtastic_Paxcount_init_default;
|
meshtastic_Paxcount pl = meshtastic_Paxcount_init_default;
|
||||||
pl.wifi = count_from_libpax.wifi_count;
|
pl.wifi = count_from_libpax.wifi_count;
|
||||||
pl.ble = count_from_libpax.ble_count;
|
pl.ble = count_from_libpax.ble_count;
|
||||||
|
@ -180,7 +180,8 @@ void NimbleBluetooth::setupService()
|
|||||||
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
|
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
|
||||||
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
|
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
|
||||||
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
||||||
logRadioCharacteristic = bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
logRadioCharacteristic =
|
||||||
|
bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
|
||||||
} else {
|
} else {
|
||||||
ToRadioCharacteristic = bleService->createCharacteristic(
|
ToRadioCharacteristic = bleService->createCharacteristic(
|
||||||
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
|
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
|
||||||
@ -189,9 +190,10 @@ void NimbleBluetooth::setupService()
|
|||||||
fromNumCharacteristic =
|
fromNumCharacteristic =
|
||||||
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
|
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
|
||||||
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
||||||
logRadioCharacteristic =
|
logRadioCharacteristic = bleService->createCharacteristic(
|
||||||
bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
|
LOGRADIO_UUID,
|
||||||
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
|
||||||
|
logRadioCharacteristic->setValue("Init");
|
||||||
}
|
}
|
||||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||||
|
|
||||||
@ -245,7 +247,7 @@ void NimbleBluetooth::sendLog(const char *logMessage)
|
|||||||
if (!bleServer || !isConnected() || strlen(logMessage) > 512) {
|
if (!bleServer || !isConnected() || strlen(logMessage) > 512) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logRadioCharacteristic->notify((uint8_t *)logMessage, strlen(logMessage));
|
logRadioCharacteristic->notify(reinterpret_cast<const uint8_t *>(logMessage), strlen(logMessage), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearNVS()
|
void clearNVS()
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
extends = esp32s3_base
|
extends = esp32s3_base
|
||||||
board = tlora-t3s3-v1
|
board = tlora-t3s3-v1
|
||||||
board_check = true
|
board_check = true
|
||||||
upload_protocol = esp-builtin
|
upload_protocol = esptool
|
||||||
|
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_base.build_flags} -D TLORA_T3S3_V1 -I variants/tlora_t3s3_v1
|
${esp32_base.build_flags} -D TLORA_T3S3_V1 -I variants/tlora_t3s3_v1
|
||||||
|
Loading…
Reference in New Issue
Block a user