diff --git a/docs/solo_features/settings_screen/settings_screen.md b/docs/solo_features/settings_screen/settings_screen.md index 8a45ddc4..cb395328 100644 --- a/docs/solo_features/settings_screen/settings_screen.md +++ b/docs/solo_features/settings_screen/settings_screen.md @@ -37,9 +37,9 @@ Press **Cancel/Back** to save and return to the home screen. | -------------- | ------------------------------ | ------------------------------------------------------------ | | Buzzer | On / Off / Auto | Auto: silences while BLE connected, re-enables on disconnect | | Volume | 1–5 | LEFT/RIGHT; preview tone plays on each change | -| DM Melody | built-in / Melody 1 / Melody 2 | Notification sound for incoming private messages | -| Channel Melody | built-in / Melody 1 / Melody 2 | Notification sound for incoming channel messages | -| AD sound | built-in / Melody 1 / Melody 2 | Sound played whenever an **advert** is received from *any* node — pairs with Auto-Advert as an audible "in range" heartbeat (see Tools › Auto-Advert). There's no *off* here; `built-in` still chirps, so silence it via the **Buzzer** setting | +| DM Melody | built-in / Melody 1 / Melody 2 / None | Notification sound for incoming private messages. `None` disables the sound for this event. | +| Channel Melody | built-in / Melody 1 / Melody 2 / None | Notification sound for incoming channel messages. `None` disables the sound for this event. | +| AD sound | built-in / Melody 1 / Melody 2 / None | Sound played whenever an **advert** is received from *any* node — pairs with Auto-Advert as an audible "in range" heartbeat (see Tools › Auto-Advert). `None` disables the sound for this event. | Melody 1 and Melody 2 are custom sequences editable in **Tools › Ringtone Editor**. diff --git a/docs/solo_features/tools_screen/tools_screen.md b/docs/solo_features/tools_screen/tools_screen.md index a9af0032..7bcf43fa 100644 --- a/docs/solo_features/tools_screen/tools_screen.md +++ b/docs/solo_features/tools_screen/tools_screen.md @@ -159,7 +159,7 @@ Saved **waypoints are included** in the export as GPX `` elements (with the Periodically broadcasts a 0-hop advert with your GPS position. Configurable interval: off / 30 s / 1 min / 2 min / 5 min / 10 min / 30 min / 1 h. A blinking **A** appears in the status bar while active. > [!TIP] -> **Audible connection heartbeat** — the device chirps each time it *receives* an advert from any node (sound chosen in **Settings › Sound › AD sound**). With Auto-Advert running on both ends (e.g. two people on a hike), each hearing the other's periodic advert becomes a hands-free "in range" beep — no need to look at the screen. It fires for **every** received advert, so in a busy mesh it can get chatty; to silence it set **Settings › Sound › Buzzer** to *Off* (or *Auto*, which mutes while a companion app is connected) — `built-in` still chirps. +> **Audible connection heartbeat** — the device chirps each time it *receives* an advert from any node (sound chosen in **Settings › Sound › AD sound**). With Auto-Advert running on both ends (e.g. two people on a hike), each hearing the other's periodic advert becomes a hands-free "in range" beep — no need to look at the screen. It fires for **every** received advert, so in a busy mesh it can get chatty; choose `None` in **Settings › Sound › AD sound** to silence just this event, or set **Settings › Sound › Buzzer** to *Off* (or *Auto*, which mutes while a companion app is connected) to silence all buzzer output. --- diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index 7507294e..7f60c9e9 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -322,7 +322,8 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no rd(&_prefs.trail_show_pace, sizeof(_prefs.trail_show_pace)); // These three were appended together; an older file leaves stray bytes here // (the old tail sentinel gets partly consumed), so clamp out-of-range values. - if (_prefs.notif_melody_ad > 2) _prefs.notif_melody_ad = 0; + // Values: 0=built-in, 1=melody1, 2=melody2, 3=none. + if (_prefs.notif_melody_ad > 3) _prefs.notif_melody_ad = 0; if (_prefs.units_imperial > 1) _prefs.units_imperial = 0; if (_prefs.trail_show_pace > 1) _prefs.trail_show_pace = 0; diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 948b2813..700b00f8 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -69,7 +69,7 @@ struct NodePrefs { // persisted to file uint8_t ringtone2_bpm_idx; uint8_t ringtone2_len; uint8_t ringtone2_notes[32]; - // Global melodies for notifications: 0=built-in, 1=melody1, 2=melody2 + // Global melodies for notifications: 0=built-in, 1=melody1, 2=melody2, 3=none uint8_t notif_melody_dm; uint8_t notif_melody_ch; uint8_t notif_melody_ad; diff --git a/examples/companion_radio/ui-new/SettingsScreen.h b/examples/companion_radio/ui-new/SettingsScreen.h index d7227e1d..6740b9da 100644 --- a/examples/companion_radio/ui-new/SettingsScreen.h +++ b/examples/companion_radio/ui-new/SettingsScreen.h @@ -89,6 +89,8 @@ class SettingsScreen : public UIScreen { static const int LOW_BAT_COUNT = 7; static const char* BATT_DISPLAY_LABELS[3]; static const int BATT_DISPLAY_COUNT = 3; + static const char* SOUND_LABELS[4]; + static const int SOUND_COUNT = 4; #if FEAT_FULL_REFRESH_SETTING static const char* EINK_FULL_REFRESH_LABELS[5]; static const int EINK_FULL_REFRESH_COUNT = 5; @@ -438,21 +440,18 @@ class SettingsScreen : public UIScreen { } else if (item == DM_MELODY) { display.print("DM sound"); display.setCursor(display.valCol(), y); - { static const char* L[] = { "built-in", "M1", "M2" }; - uint8_t v = p ? p->notif_melody_dm : 0; - display.print(L[v < 3 ? v : 0]); } + { uint8_t v = p ? p->notif_melody_dm : 0; + display.print(SOUND_LABELS[v < SOUND_COUNT ? v : 0]); } } else if (item == CH_MELODY) { display.print("Ch sound"); display.setCursor(display.valCol(), y); - { static const char* L[] = { "built-in", "M1", "M2" }; - uint8_t v = p ? p->notif_melody_ch : 0; - display.print(L[v < 3 ? v : 0]); } + { uint8_t v = p ? p->notif_melody_ch : 0; + display.print(SOUND_LABELS[v < SOUND_COUNT ? v : 0]); } } else if (item == AD_SOUND) { display.print("AD sound"); display.setCursor(display.valCol(), y); - { static const char* L[] = { "built-in", "M1", "M2" }; - uint8_t v = p ? p->notif_melody_ad : 0; - display.print(L[v < 3 ? v : 0]); } + { uint8_t v = p ? p->notif_melody_ad : 0; + display.print(SOUND_LABELS[v < SOUND_COUNT ? v : 0]); } } else if (isHomePage(item)) { if (p) ensurePageOrderInit(p); int pos = homePagePosition(item, p); @@ -696,15 +695,15 @@ public: return right || left; } if (_selected == DM_MELODY && p && (left || right || enter)) { - p->notif_melody_dm = (p->notif_melody_dm + (left ? 2 : 1)) % 3; + p->notif_melody_dm = (p->notif_melody_dm + (left ? SOUND_COUNT - 1 : 1)) % SOUND_COUNT; _dirty = true; return true; } if (_selected == CH_MELODY && p && (left || right || enter)) { - p->notif_melody_ch = (p->notif_melody_ch + (left ? 2 : 1)) % 3; + p->notif_melody_ch = (p->notif_melody_ch + (left ? SOUND_COUNT - 1 : 1)) % SOUND_COUNT; _dirty = true; return true; } if (_selected == AD_SOUND && p && (left || right || enter)) { - p->notif_melody_ad = (p->notif_melody_ad + (left ? 2 : 1)) % 3; + p->notif_melody_ad = (p->notif_melody_ad + (left ? SOUND_COUNT - 1 : 1)) % SOUND_COUNT; _dirty = true; return true; } if (isHomePage(_selected) && p) { @@ -839,6 +838,7 @@ const char* SettingsScreen::AUTO_OFF_LABELS[5] = { "5s", "15s", "30s", "60s" const uint16_t SettingsScreen::LOW_BAT_OPTS[7] = { 0, 3000, 3100, 3200, 3300, 3400, 3500 }; const char* SettingsScreen::LOW_BAT_LABELS[7] = { "off", "3.0V", "3.1V", "3.2V", "3.3V", "3.4V", "3.5V" }; const char* SettingsScreen::BATT_DISPLAY_LABELS[3] = { "icon", "%", "V" }; +const char* SettingsScreen::SOUND_LABELS[4] = { "built-in", "M1", "M2", "None" }; #if FEAT_FULL_REFRESH_SETTING const char* SettingsScreen::EINK_FULL_REFRESH_LABELS[5] = { "off", "5", "10", "20", "30" }; #endif diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index cedecd00..465a8c77 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1413,7 +1413,9 @@ switch(t){ { slot = _node_prefs->dm_melody[i].slot; break; } } bool custom_played = false; - if (slot > 0 && _node_prefs) { + if (slot == 3) { + custom_played = true; // explicit silence + } else if (slot > 0 && _node_prefs) { if (buzzer.isPlaying()) buzzer.stop(); // stop before overwriting _notif_mel_buf buildMelodyFromPrefs(_node_prefs, slot, _notif_mel_buf, sizeof(_notif_mel_buf)); if (_notif_mel_buf[0]) { @@ -1449,7 +1451,9 @@ switch(t){ slot = (_node_prefs->ch_notif_melody_2 & mask) ? 2 : 1; } bool custom_played = false; - if (slot > 0 && _node_prefs) { + if (slot == 3) { + custom_played = true; // explicit silence + } else if (slot > 0 && _node_prefs) { if (buzzer.isPlaying()) buzzer.stop(); // stop before overwriting _notif_mel_buf buildMelodyFromPrefs(_node_prefs, slot, _notif_mel_buf, sizeof(_notif_mel_buf)); if (_notif_mel_buf[0]) { @@ -1469,7 +1473,9 @@ switch(t){ if (!buzzer.isQuiet()) { int slot = _node_prefs ? (int)_node_prefs->notif_melody_ad : 0; bool custom_played = false; - if (slot > 0 && _node_prefs) { + if (slot == 3) { + custom_played = true; // explicit silence + } else if (slot > 0 && _node_prefs) { if (buzzer.isPlaying()) buzzer.stop(); // stop before overwriting _notif_mel_buf buildMelodyFromPrefs(_node_prefs, slot, _notif_mel_buf, sizeof(_notif_mel_buf)); if (_notif_mel_buf[0]) {