mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-18 17:31:42 +10:00
Merge branch 'master' into nomad-gemini
This commit is contained in:
@@ -630,6 +630,7 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
#if USERPREFS_EVENT_MODE
|
||||
// If we're in event mode, nobody is a Router or Repeater
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_LATE ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
|
||||
}
|
||||
@@ -1198,7 +1199,7 @@ void AdminModule::reboot(int32_t seconds)
|
||||
{
|
||||
LOG_INFO("Reboot in %d seconds", seconds);
|
||||
if (screen)
|
||||
screen->showOverlayBanner("Rebooting...", 0); // stays on screen
|
||||
screen->showSimpleBanner("Rebooting...", 0); // stays on screen
|
||||
rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000);
|
||||
}
|
||||
|
||||
@@ -1334,12 +1335,6 @@ void AdminModule::handleSendInputEvent(const meshtastic_AdminMessage_InputEvent
|
||||
LOG_DEBUG("Processing input event: event_code=%u, kb_char=%u, touch_x=%u, touch_y=%u", inputEvent.event_code,
|
||||
inputEvent.kb_char, inputEvent.touch_x, inputEvent.touch_y);
|
||||
|
||||
// Validate input parameters
|
||||
if (inputEvent.event_code > INPUT_BROKER_ANYKEY) {
|
||||
LOG_WARN("Invalid input event code: %u", inputEvent.event_code);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create InputEvent for injection
|
||||
InputEvent event = {.inputEvent = (input_broker_event)inputEvent.event_code,
|
||||
.kbchar = (unsigned char)inputEvent.kb_char,
|
||||
|
||||
@@ -154,7 +154,7 @@ int CannedMessageModule::splitConfiguredMessages()
|
||||
}
|
||||
void CannedMessageModule::drawHeader(OLEDDisplay *display, int16_t x, int16_t y, char *buffer)
|
||||
{
|
||||
if (display->getWidth() > 128) {
|
||||
if (graphics::isHighResolution) {
|
||||
if (this->dest == NODENUM_BROADCAST) {
|
||||
display->drawStringf(x, y, buffer, "To: Broadcast@%s", channels.getName(this->channel));
|
||||
} else {
|
||||
@@ -245,12 +245,15 @@ void CannedMessageModule::updateDestinationSelectionList()
|
||||
}
|
||||
}
|
||||
|
||||
/* As the nodeDB is sorted, can skip this step
|
||||
// Sort by favorite, then last heard
|
||||
std::sort(this->filteredNodes.begin(), this->filteredNodes.end(), [](const NodeEntry &a, const NodeEntry &b) {
|
||||
if (a.node->is_favorite != b.node->is_favorite)
|
||||
return a.node->is_favorite > b.node->is_favorite;
|
||||
return a.lastHeard < b.lastHeard;
|
||||
});
|
||||
*/
|
||||
|
||||
scrollIndex = 0; // Show first result at the top
|
||||
destIndex = 0; // Highlight the first entry
|
||||
if (nodesChanged && runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) {
|
||||
@@ -387,6 +390,7 @@ bool CannedMessageModule::handleTabSwitch(const InputEvent *event)
|
||||
// RESTORE THIS!
|
||||
if (runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION)
|
||||
updateDestinationSelectionList();
|
||||
requestFocus();
|
||||
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
@@ -438,25 +442,34 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
|
||||
return 1;
|
||||
}
|
||||
|
||||
// UP
|
||||
if (isUp && destIndex > 0) {
|
||||
destIndex--;
|
||||
if (isUp) {
|
||||
if (destIndex > 0) {
|
||||
destIndex--;
|
||||
} else if (totalEntries > 0) {
|
||||
destIndex = totalEntries - 1;
|
||||
}
|
||||
|
||||
if ((destIndex / columns) < scrollIndex)
|
||||
scrollIndex = destIndex / columns;
|
||||
else if ((destIndex / columns) >= (scrollIndex + visibleRows))
|
||||
scrollIndex = (destIndex / columns) - visibleRows + 1;
|
||||
|
||||
screen->forceDisplay();
|
||||
screen->forceDisplay(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// DOWN
|
||||
if (isDown && destIndex + 1 < totalEntries) {
|
||||
destIndex++;
|
||||
if (isDown) {
|
||||
if (destIndex + 1 < totalEntries) {
|
||||
destIndex++;
|
||||
} else if (totalEntries > 0) {
|
||||
destIndex = 0;
|
||||
scrollIndex = 0;
|
||||
}
|
||||
|
||||
if ((destIndex / columns) >= (scrollIndex + visibleRows))
|
||||
scrollIndex = (destIndex / columns) - visibleRows + 1;
|
||||
|
||||
screen->forceDisplay();
|
||||
screen->forceDisplay(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -478,7 +491,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
|
||||
|
||||
runState = returnToCannedList ? CANNED_MESSAGE_RUN_STATE_ACTIVE : CANNED_MESSAGE_RUN_STATE_FREETEXT;
|
||||
returnToCannedList = false;
|
||||
screen->forceDisplay();
|
||||
screen->forceDisplay(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -491,7 +504,7 @@ int CannedMessageModule::handleDestinationSelectionInput(const InputEvent *event
|
||||
// UIFrameEvent e;
|
||||
// e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
// notifyObservers(&e);
|
||||
screen->forceDisplay();
|
||||
screen->forceDisplay(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -703,7 +716,7 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
||||
}
|
||||
|
||||
// Backspace
|
||||
if (event->inputEvent == INPUT_BROKER_BACK) {
|
||||
if (event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() > 0) {
|
||||
payload = 0x08;
|
||||
lastTouchMillis = millis();
|
||||
runOnce();
|
||||
@@ -726,7 +739,8 @@ bool CannedMessageModule::handleFreeTextInput(const InputEvent *event)
|
||||
}
|
||||
|
||||
// Cancel (dismiss freetext screen)
|
||||
if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG) {
|
||||
if (event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG ||
|
||||
(event->inputEvent == INPUT_BROKER_BACK && this->freetext.length() == 0)) {
|
||||
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
freetext = "";
|
||||
cursor = 0;
|
||||
@@ -976,6 +990,7 @@ int32_t CannedMessageModule::runOnce()
|
||||
}
|
||||
this->cursor--;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
break;
|
||||
case INPUT_BROKER_MSG_TAB: // Tab key: handled by input handler
|
||||
@@ -986,6 +1001,7 @@ int32_t CannedMessageModule::runOnce()
|
||||
default:
|
||||
// Only insert ASCII printable characters (32–126)
|
||||
if (this->payload >= 32 && this->payload <= 126) {
|
||||
requestFocus();
|
||||
if (this->cursor == this->freetext.length()) {
|
||||
this->freetext += (char)this->payload;
|
||||
} else {
|
||||
@@ -2061,4 +2077,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor)
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -362,9 +362,8 @@ ExternalNotificationModule::ExternalNotificationModule()
|
||||
if (nodeDB->loadProto(rtttlConfigFile, meshtastic_RTTTLConfig_size, sizeof(meshtastic_RTTTLConfig),
|
||||
&meshtastic_RTTTLConfig_msg, &rtttlConfig) != LoadFileResult::LOAD_SUCCESS) {
|
||||
memset(rtttlConfig.ringtone, 0, sizeof(rtttlConfig.ringtone));
|
||||
strncpy(rtttlConfig.ringtone,
|
||||
"24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p",
|
||||
sizeof(rtttlConfig.ringtone));
|
||||
// The default ringtone is always loaded from userPrefs.jsonc
|
||||
strncpy(rtttlConfig.ringtone, USERPREFS_RINGTONE_RTTTL, sizeof(rtttlConfig.ringtone));
|
||||
}
|
||||
|
||||
LOG_INFO("Init External Notification Module");
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#include "KeyVerificationModule.h"
|
||||
#include "MeshService.h"
|
||||
#include "RTC.h"
|
||||
#include "graphics/draw/MenuHandler.h"
|
||||
#include "main.h"
|
||||
#include "meshUtils.h"
|
||||
#include "modules/AdminModule.h"
|
||||
#include <SHA256.h>
|
||||
|
||||
@@ -48,18 +50,22 @@ AdminMessageHandleResult KeyVerificationModule::handleAdminMessageForModule(cons
|
||||
bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_KeyVerification *r)
|
||||
{
|
||||
updateState();
|
||||
if (mp.pki_encrypted == false)
|
||||
if (mp.pki_encrypted == false) {
|
||||
return false;
|
||||
if (mp.from != currentRemoteNode) // because the inital connection request is handled in allocReply()
|
||||
}
|
||||
if (mp.from != currentRemoteNode) { // because the inital connection request is handled in allocReply()
|
||||
return false;
|
||||
}
|
||||
if (currentState == KEY_VERIFICATION_IDLE) {
|
||||
return false; // if we're idle, the only acceptable message is an init, which should be handled by allocReply()
|
||||
}
|
||||
|
||||
} else if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 &&
|
||||
r->hash1.size == 0) {
|
||||
if (currentState == KEY_VERIFICATION_SENDER_HAS_INITIATED && r->nonce == currentNonce && r->hash2.size == 32 &&
|
||||
r->hash1.size == 0) {
|
||||
memcpy(hash2, r->hash2.bytes, 32);
|
||||
if (screen)
|
||||
screen->showOverlayBanner("Enter Security Number", 30000);
|
||||
IF_SCREEN(screen->showNumberPicker("Enter Security Number", 60000, 6, [](int number_picked) -> void {
|
||||
keyVerificationModule->processSecurityNumber(number_picked);
|
||||
});)
|
||||
|
||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||
cn->level = meshtastic_LogRecord_Level_WARNING;
|
||||
@@ -79,16 +85,20 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
|
||||
memset(message, 0, sizeof(message));
|
||||
sprintf(message, "Verification: \n");
|
||||
generateVerificationCode(message + 15);
|
||||
sprintf(message + 24, "\nACCEPT\nREJECT");
|
||||
LOG_INFO("Hash1 matches!");
|
||||
if (screen) {
|
||||
screen->showOverlayBanner(message, 30000, 2, [=](int selected) {
|
||||
if (selected == 0) {
|
||||
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
|
||||
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
}
|
||||
});
|
||||
}
|
||||
static const char *optionsArray[] = {"Reject", "Accept"};
|
||||
// Don't try to put the array definition in the macro. Does not work with curly braces.
|
||||
IF_SCREEN(graphics::BannerOverlayOptions options; options.message = message; options.durationMs = 30000;
|
||||
options.optionsArrayPtr = optionsArray; options.optionsCount = 2;
|
||||
options.notificationType = graphics::notificationTypeEnum::selection_picker;
|
||||
options.bannerCallback =
|
||||
[=](int selected) {
|
||||
if (selected == 1) {
|
||||
auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode);
|
||||
remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;
|
||||
}
|
||||
};
|
||||
screen->showOverlayBanner(options);)
|
||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||
cn->level = meshtastic_LogRecord_Level_WARNING;
|
||||
sprintf(cn->message, "Final confirmation for incoming manual key verification %s", message);
|
||||
@@ -113,6 +123,7 @@ bool KeyVerificationModule::sendInitialRequest(NodeNum remoteNode)
|
||||
// generate nonce
|
||||
updateState();
|
||||
if (currentState != KEY_VERIFICATION_IDLE) {
|
||||
IF_SCREEN(graphics::menuHandler::menuQueue = graphics::menuHandler::throttle_message;)
|
||||
return false;
|
||||
}
|
||||
currentNonce = random();
|
||||
@@ -183,11 +194,8 @@ meshtastic_MeshPacket *KeyVerificationModule::allocReply()
|
||||
responsePacket = allocDataProtobuf(response);
|
||||
|
||||
responsePacket->pki_encrypted = true;
|
||||
if (screen) {
|
||||
snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000);
|
||||
screen->showOverlayBanner(message, 30000);
|
||||
LOG_WARN("%s", message);
|
||||
}
|
||||
IF_SCREEN(snprintf(message, 25, "Security Number \n%03u %03u", currentSecurityNumber / 1000, currentSecurityNumber % 1000);
|
||||
screen->showSimpleBanner(message, 30000); LOG_WARN("%s", message);)
|
||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||
cn->level = meshtastic_LogRecord_Level_WARNING;
|
||||
sprintf(cn->message, "Incoming Key Verification.\nSecurity Number\n%03u %03u", currentSecurityNumber / 1000,
|
||||
@@ -251,12 +259,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
|
||||
p->priority = meshtastic_MeshPacket_Priority_HIGH;
|
||||
service->sendToMesh(p, RX_SRC_LOCAL, true);
|
||||
currentState = KEY_VERIFICATION_SENDER_AWAITING_USER;
|
||||
memset(message, 0, sizeof(message));
|
||||
sprintf(message, "Verification: \n");
|
||||
generateVerificationCode(message + 15); // send the toPhone packet
|
||||
if (screen) {
|
||||
screen->showOverlayBanner(message, 30000);
|
||||
}
|
||||
IF_SCREEN(screen->requestMenu(graphics::menuHandler::key_verification_final_prompt);)
|
||||
meshtastic_ClientNotification *cn = clientNotificationPool.allocZeroed();
|
||||
cn->level = meshtastic_LogRecord_Level_WARNING;
|
||||
sprintf(cn->message, "Final confirmation for outgoing manual key verification %s", message);
|
||||
@@ -275,7 +278,7 @@ void KeyVerificationModule::processSecurityNumber(uint32_t incomingNumber)
|
||||
void KeyVerificationModule::updateState()
|
||||
{
|
||||
if (currentState != KEY_VERIFICATION_IDLE) {
|
||||
// check for the 30 second timeout
|
||||
// check for the 60 second timeout
|
||||
if (currentNonceTimestamp < getTime() - 60) {
|
||||
resetToIdle();
|
||||
} else {
|
||||
|
||||
@@ -27,6 +27,8 @@ class KeyVerificationModule : public ProtobufModule<meshtastic_KeyVerification>
|
||||
}*/
|
||||
virtual bool wantUIFrame() { return false; };
|
||||
bool sendInitialRequest(NodeNum remoteNode);
|
||||
void generateVerificationCode(char *); // fills char with the user readable verification code
|
||||
uint32_t getCurrentRemoteNode() { return currentRemoteNode; }
|
||||
|
||||
protected:
|
||||
/* Called to handle a particular incoming message
|
||||
@@ -56,9 +58,8 @@ class KeyVerificationModule : public ProtobufModule<meshtastic_KeyVerification>
|
||||
char message[40] = {0};
|
||||
|
||||
void processSecurityNumber(uint32_t);
|
||||
void updateState(); // check the timeouts and maybe reset the state to idle
|
||||
void resetToIdle(); // Zero out module state
|
||||
void generateVerificationCode(char *); // fills char with the user readable verification code
|
||||
void updateState(); // check the timeouts and maybe reset the state to idle
|
||||
void resetToIdle(); // Zero out module state
|
||||
};
|
||||
|
||||
extern KeyVerificationModule *keyVerificationModule;
|
||||
@@ -53,6 +53,7 @@
|
||||
#endif
|
||||
#if ARCH_PORTDUINO
|
||||
#include "input/LinuxInputImpl.h"
|
||||
#include "input/SeesawRotary.h"
|
||||
#include "modules/Telemetry/HostMetrics.h"
|
||||
#if !MESHTASTIC_EXCLUDE_STOREFORWARD
|
||||
#include "modules/StoreForwardModule.h"
|
||||
@@ -163,7 +164,6 @@ void setupModules()
|
||||
// Example: Put your module here
|
||||
// new ReplyModule();
|
||||
#if (HAS_BUTTON || ARCH_PORTDUINO) && !MESHTASTIC_EXCLUDE_INPUTBROKER
|
||||
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
|
||||
if (!rotaryEncoderInterruptImpl1->init()) {
|
||||
@@ -189,6 +189,11 @@ void setupModules()
|
||||
#endif // HAS_BUTTON
|
||||
#if ARCH_PORTDUINO
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_COLOR) {
|
||||
seesawRotary = new SeesawRotary("SeesawRotary");
|
||||
if (!seesawRotary->init()) {
|
||||
delete seesawRotary;
|
||||
seesawRotary = nullptr;
|
||||
}
|
||||
aLinuxInputImpl = new LinuxInputImpl();
|
||||
aLinuxInputImpl->init();
|
||||
}
|
||||
@@ -213,11 +218,14 @@ void setupModules()
|
||||
#if HAS_TELEMETRY
|
||||
new DeviceTelemetryModule();
|
||||
#endif
|
||||
// TODO: How to improve this?
|
||||
#if HAS_SENSOR && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR
|
||||
new EnvironmentTelemetryModule();
|
||||
#if __has_include("Adafruit_PM25AQI.h")
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) {
|
||||
new AirQualityTelemetryModule();
|
||||
}
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_HEALTH_TELEMETRY
|
||||
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MAX30102].first > 0 ||
|
||||
nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_MLX90614].first > 0) {
|
||||
|
||||
@@ -47,7 +47,7 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
|
||||
bool isMuted = externalNotificationModule->getMute();
|
||||
externalNotificationModule->setMute(!isMuted);
|
||||
IF_SCREEN(graphics::isMuted = !isMuted; if (!isMuted) externalNotificationModule->stopNow();
|
||||
screen->showOverlayBanner(isMuted ? "Notifications\nEnabled" : "Notifications\nDisabled", 3000);)
|
||||
screen->showSimpleBanner(isMuted ? "Notifications\nEnabled" : "Notifications\nDisabled", 3000);)
|
||||
}
|
||||
return 0;
|
||||
// Bluetooth
|
||||
@@ -58,24 +58,24 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
|
||||
#if defined(ARDUINO_ARCH_NRF52)
|
||||
if (!config.bluetooth.enabled) {
|
||||
disableBluetooth();
|
||||
IF_SCREEN(screen->showOverlayBanner("Bluetooth OFF\nRebooting", 3000));
|
||||
IF_SCREEN(screen->showSimpleBanner("Bluetooth OFF\nRebooting", 3000));
|
||||
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 2000;
|
||||
} else {
|
||||
IF_SCREEN(screen->showOverlayBanner("Bluetooth ON\nRebooting", 3000));
|
||||
IF_SCREEN(screen->showSimpleBanner("Bluetooth ON\nRebooting", 3000));
|
||||
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
|
||||
}
|
||||
#else
|
||||
if (!config.bluetooth.enabled) {
|
||||
disableBluetooth();
|
||||
IF_SCREEN(screen->showOverlayBanner("Bluetooth OFF", 3000));
|
||||
IF_SCREEN(screen->showSimpleBanner("Bluetooth OFF", 3000));
|
||||
} else {
|
||||
IF_SCREEN(screen->showOverlayBanner("Bluetooth ON\nRebooting", 3000));
|
||||
IF_SCREEN(screen->showSimpleBanner("Bluetooth ON\nRebooting", 3000));
|
||||
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
case INPUT_BROKER_MSG_REBOOT:
|
||||
IF_SCREEN(screen->showOverlayBanner("Rebooting...", 0));
|
||||
IF_SCREEN(screen->showSimpleBanner("Rebooting...", 0));
|
||||
nodeDB->saveToDisk();
|
||||
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
|
||||
// runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
@@ -92,7 +92,7 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
|
||||
gps->toggleGpsMode();
|
||||
const char *msg =
|
||||
(config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) ? "GPS Enabled" : "GPS Disabled";
|
||||
IF_SCREEN(screen->forceDisplay(); screen->showOverlayBanner(msg, 3000);)
|
||||
IF_SCREEN(screen->forceDisplay(); screen->showSimpleBanner(msg, 3000);)
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
@@ -100,19 +100,23 @@ int SystemCommandsModule::handleInputEvent(const InputEvent *event)
|
||||
case INPUT_BROKER_SEND_PING:
|
||||
service->refreshLocalMeshNode();
|
||||
if (service->trySendPosition(NODENUM_BROADCAST, true)) {
|
||||
IF_SCREEN(screen->showOverlayBanner("Position\nUpdate Sent", 3000));
|
||||
IF_SCREEN(screen->showSimpleBanner("Position\nSent", 3000));
|
||||
} else {
|
||||
IF_SCREEN(screen->showOverlayBanner("Node Info\nUpdate Sent", 3000));
|
||||
IF_SCREEN(screen->showSimpleBanner("Node Info\nSent", 3000));
|
||||
}
|
||||
return true;
|
||||
// Power control
|
||||
case INPUT_BROKER_SHUTDOWN:
|
||||
LOG_ERROR("Shutting down");
|
||||
IF_SCREEN(screen->showOverlayBanner("Shutting down..."));
|
||||
LOG_ERROR("Shutting Down");
|
||||
IF_SCREEN(screen->showSimpleBanner("Shutting Down..."));
|
||||
nodeDB->saveToDisk();
|
||||
shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000;
|
||||
// runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
return true;
|
||||
|
||||
default:
|
||||
// No other input events handled here
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr);
|
||||
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only);
|
||||
}
|
||||
#if __has_include(<Adafruit_AHTX0.h>)
|
||||
#include "Sensor/AHT10.h"
|
||||
@@ -358,7 +358,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
|
||||
int line = 1;
|
||||
|
||||
// === Set Title
|
||||
const char *titleStr = (SCREEN_WIDTH > 128) ? "Environment" : "Env.";
|
||||
const char *titleStr = (graphics::isHighResolution) ? "Environment" : "Env.";
|
||||
|
||||
// === Header ===
|
||||
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||
@@ -450,7 +450,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
|
||||
|
||||
if (isOwnTelemetry && bannerMsg && isCooldownOver) {
|
||||
LOG_INFO("drawFrame: IAQ %d (own) — showing banner: %s", m.iaq, bannerMsg);
|
||||
screen->showOverlayBanner(bannerMsg, 3000);
|
||||
screen->showSimpleBanner(bannerMsg, 3000);
|
||||
|
||||
// Only buzz if IAQ is over 200
|
||||
if (m.iaq > 200 && moduleConfig.external_notification.enabled && !externalNotificationModule->getMute()) {
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr);
|
||||
extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const char *titleStr, bool battery_only);
|
||||
}
|
||||
|
||||
int32_t PowerTelemetryModule::runOnce()
|
||||
@@ -115,7 +115,7 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
|
||||
int line = 1;
|
||||
|
||||
// === Set Title
|
||||
const char *titleStr = (SCREEN_WIDTH > 128) ? "Power Telem." : "Power";
|
||||
const char *titleStr = (graphics::isHighResolution) ? "Power Telem." : "Power";
|
||||
|
||||
// === Header ===
|
||||
graphics::drawCommonHeader(display, x, y, titleStr);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "MAX17048Sensor.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_STM32WL) && __has_include(<Adafruit_MAX1704X.h>)
|
||||
#if !MESHTASTIC_EXCLUDE_I2C && __has_include(<Adafruit_MAX1704X.h>)
|
||||
|
||||
MAX17048Singleton *MAX17048Singleton::GetInstance()
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_I2C && !defined(ARCH_STM32WL) && __has_include(<Adafruit_MAX1704X.h>)
|
||||
#if !MESHTASTIC_EXCLUDE_I2C && __has_include(<Adafruit_MAX1704X.h>)
|
||||
|
||||
// Samples to store in a buffer to determine if the battery is charging or discharging
|
||||
#define MAX17048_CHARGING_SAMPLES 3
|
||||
|
||||
@@ -20,4 +20,15 @@ bool NullSensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t NullSensor::getBusVoltageMv()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int16_t NullSensor::getCurrentMa()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -4,9 +4,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "../mesh/generated/meshtastic/telemetry.pb.h"
|
||||
#include "TelemetrySensor.h"
|
||||
|
||||
class NullSensor : public TelemetrySensor
|
||||
#include "CurrentSensor.h"
|
||||
#include "TelemetrySensor.h"
|
||||
#include "VoltageSensor.h"
|
||||
|
||||
class NullSensor : public TelemetrySensor, VoltageSensor, CurrentSensor
|
||||
{
|
||||
|
||||
protected:
|
||||
@@ -17,6 +20,9 @@ class NullSensor : public TelemetrySensor
|
||||
virtual int32_t runOnce() override;
|
||||
virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
|
||||
int32_t runTrigger() { return 0; }
|
||||
|
||||
virtual uint16_t getBusVoltageMv() override;
|
||||
virtual int16_t getCurrentMa() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "PowerFSM.h"
|
||||
#include "buzz.h"
|
||||
#include "configuration.h"
|
||||
#include "graphics/Screen.h"
|
||||
TextMessageModule *textMessageModule;
|
||||
|
||||
ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
|
||||
@@ -17,7 +18,10 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
|
||||
devicestate.rx_text_message = mp;
|
||||
devicestate.has_rx_text_message = true;
|
||||
|
||||
powerFSM.trigger(EVENT_RECEIVED_MSG);
|
||||
// Only trigger screen wake if configuration allows it
|
||||
if (shouldWakeOnReceivedMessage()) {
|
||||
powerFSM.trigger(EVENT_RECEIVED_MSG);
|
||||
}
|
||||
notifyObservers(&mp);
|
||||
|
||||
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
|
||||
|
||||
@@ -137,10 +137,14 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
if (ourNode && (nodeDB->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));
|
||||
if (uiconfig.compass_mode == meshtastic_CompassMode_FREEZE_HEADING) {
|
||||
myHeading = 0;
|
||||
} else {
|
||||
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));
|
||||
}
|
||||
graphics::CompassRenderer::drawCompassNorth(display, compassX, compassY, myHeading, (compassDiam / 2));
|
||||
|
||||
// Compass bearing to waypoint
|
||||
@@ -148,7 +152,7 @@ void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state,
|
||||
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)
|
||||
if (uiconfig.compass_mode != meshtastic_CompassMode_FREEZE_HEADING)
|
||||
bearingToOther -= myHeading;
|
||||
graphics::CompassRenderer::drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user