Add waypoint & geofence support with notifications for BaseUI and InkHUD (#10920)

* Implement GeofenceModule for waypoint crossing notifications and integrate with existing modules

* Waypoint Applet Initial Support on InkHUD

* undo tile change

* Update screen when Waypoint shows or dissapears

* Merge branch 'develop' into waypoint-geofence

* Geofence on InkHUD

* Update MapTile.h

* Update WaypointStore.cpp

* Notifications

* remove GF from waypoint screen

* Prevent Focus from closing the notifiaction banner

* Trunk fix

* cleanup

* undo merge conflix mistake

* Waypoint screen on BaseUI

* Focus preserve fix

* UI bugs

* Allow Inkhud to remove waypoint

* Respect Locked Waypoints

* Trunk fix

* Update WaypointStore.cpp

* Use 8-digit hex formatting for waypoint IDs.

0x%x was inconsistent with the repo's own convention (0x%08x for 32-bit IDs, used elsewhere in this file). Fixed here and in two other spots I found with the same issue (WaypointModule.cpp, GeofenceModule.cpp).

* Update ExternalNotificationModule.cpp

* Reject invalid surrogate codepoints in waypoint icon rendering

* Update WaypointModule.cpp

* Update WaypointStore.cpp

* Update WaypointStore.cpp

* Update WaypointStore.cpp

* trunk fix

* fix warnings

* power.h rename to Power.h

* Update Power.h

* Fix executable bit on bin/lint-ifdef-complexity.sh

Lost during a prior merge from develop (Windows checkout doesn't
preserve file mode), causing "execve failed: Permission denied" in
the Trunk Check Runner CI job. develop has this file at 100755;
restoring that here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Update README.md

* Clean up waypoint and geofence integration

* Minimize waypoint and geofence implementation

* removed unnecessary gating

* Geofence alert

* trunk fix

* Update test_main.cpp

* Update WaypointStore.cpp

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-08-26 16:17:28 +00:00
1 parent 8eda86045b
commit cd6ac90f7e
51 files changed
+3005 -222

No files matched your search

+77 -27
View File
@@ -449,11 +449,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
(moduleConfig.external_notification.alert_message_buzzer && !is_muted)));
if (genericShouldAlert || vibraShouldAlert || buzzerShouldAlert) {
nagCycleCutoff = millis() + (moduleConfig.external_notification.nag_timeout
? (moduleConfig.external_notification.nag_timeout * 1000)
: moduleConfig.external_notification.output_ms);
LOG_INFO("Toggling nagCycleCutoff to %lu", nagCycleCutoff);
isNagging = true;
armNagCycle();
}
if (genericShouldAlert) {
@@ -463,19 +459,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
if (vibraShouldAlert) {
LOG_INFO("externalNotificationModule - Vibra alert");
#ifdef HAS_DRV2605
// Set DRV2605 waveform when vibration alert is triggered
drv.setWaveform(0, 16); // Long buzzer 100%
drv.setWaveform(1, 0); // Pause
drv.setWaveform(2, 16);
drv.setWaveform(3, 0);
drv.setWaveform(4, 16);
drv.setWaveform(5, 0);
drv.setWaveform(6, 16);
drv.setWaveform(7, 0);
drv.go();
#endif
setExternalState(1, true);
triggerVibraOutput();
}
if (buzzerShouldAlert) {
@@ -484,15 +468,7 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
LOG_INFO("Buzzer suppressed: mode DIRECT_MSG_ONLY");
} else {
// Buzz if buzzer mode is not in DIRECT_MSG_ONLY or is DM to us
if (moduleConfig.external_notification.use_i2s_as_buzzer) {
#ifdef HAS_I2S
audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone));
#endif
} else if (moduleConfig.external_notification.use_pwm) {
rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
} else {
setExternalState(2, true);
}
triggerBuzzerOutput();
}
}
@@ -505,6 +481,80 @@ ProcessMessage ExternalNotificationModule::handleReceived(const meshtastic_MeshP
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
void ExternalNotificationModule::triggerBuzzerOutput()
{
if (moduleConfig.external_notification.use_i2s_as_buzzer) {
#ifdef HAS_I2S
audioThread->beginRttl(rtttlConfig.ringtone, strlen_P(rtttlConfig.ringtone));
#endif
} else if (moduleConfig.external_notification.use_pwm) {
rtttl::begin(config.device.buzzer_gpio, rtttlConfig.ringtone);
} else {
setExternalState(2, true);
}
}
void ExternalNotificationModule::triggerVibraOutput()
{
#ifdef HAS_DRV2605
drv.setWaveform(0, 16);
drv.setWaveform(1, 0);
drv.setWaveform(2, 16);
drv.setWaveform(3, 0);
drv.setWaveform(4, 16);
drv.setWaveform(5, 0);
drv.setWaveform(6, 16);
drv.setWaveform(7, 0);
drv.go();
#endif
setExternalState(1, true);
}
void ExternalNotificationModule::armNagCycle()
{
const uint32_t durationMs = moduleConfig.external_notification.nag_timeout
? moduleConfig.external_notification.nag_timeout * 1000UL
: moduleConfig.external_notification.output_ms;
nagCycleCutoff = millis() + durationMs;
LOG_INFO("Toggling nagCycleCutoff to %lu", nagCycleCutoff);
isNagging = true;
}
void ExternalNotificationModule::startNotification()
{
if (!moduleConfig.external_notification.enabled || isSilenced)
return;
// Waypoint and geofence events are neither direct messages nor bells.
const bool buzzerModeIsDirectOnly = (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DIRECT_MSG_ONLY);
const bool generic = moduleConfig.external_notification.alert_message;
const bool vibra = moduleConfig.external_notification.alert_message_vibra;
const bool buzzer = canBuzz() && moduleConfig.external_notification.alert_message_buzzer && !buzzerModeIsDirectOnly;
if (canBuzz() && moduleConfig.external_notification.alert_message_buzzer && buzzerModeIsDirectOnly)
LOG_INFO("Non-message buzzer was suppressed because buzzer mode DIRECT_MSG_ONLY");
if (!generic && !vibra && !buzzer)
return;
buzzerShouldAlert |= buzzer;
armNagCycle();
if (generic) {
LOG_INFO("externalNotificationModule - Generic alert");
setExternalState(0, true);
}
if (vibra) {
LOG_INFO("externalNotificationModule - Vibra alert");
triggerVibraOutput();
}
if (buzzer) {
LOG_INFO("externalNotificationModule - Buzzer alert");
triggerBuzzerOutput();
}
setIntervalFromNow(0); // run once so the nag/stop lifecycle in runOnce() takes over
}
/**
* @brief An admin message arrived to AdminModule. We are asked whether we want to handle that.
*