diff --git a/MDUV380_firmware/application/include/user_interface/uiUtilities.h b/MDUV380_firmware/application/include/user_interface/uiUtilities.h index f4faced..1d55f8d 100644 --- a/MDUV380_firmware/application/include/user_interface/uiUtilities.h +++ b/MDUV380_firmware/application/include/user_interface/uiUtilities.h @@ -243,6 +243,7 @@ void acceptPrivateCall(uint32_t id, int timeslot); bool rebuildVoicePromptOnExtraLongSK1(uiEvent_t *ev); bool repeatVoicePromptOnSK1(uiEvent_t *ev); bool handleMonitorMode(uiEvent_t *ev); +void toggleAudioMute(void); void uiUtilityDisplayInformation(const char *str, displayInformation_t line, int8_t yOverride); void uiUtilityRenderQSODataAndUpdateScreen(void); diff --git a/MDUV380_firmware/application/source/applicationMain.c b/MDUV380_firmware/application/source/applicationMain.c index bd5493d..0f764d5 100644 --- a/MDUV380_firmware/application/source/applicationMain.c +++ b/MDUV380_firmware/application/source/applicationMain.c @@ -36,6 +36,7 @@ #include "interfaces/adc.h" #include "interfaces/batteryRAM.h" #include "hardware/SPI_Flash.h" +#include "io/Leds.h" #include "interfaces/adc.h" #include "hardware/radioHardwareInterface.h" @@ -1432,6 +1433,27 @@ void applicationMainTask(void) updateVolumeGain(currentMenu); } + // Mute indicator: while muted, flash the RED LED for 0.5s every 2s. + // This overrides the normal RX/TX LED while muted. + { + static bool muteFlashActive = false; + + if (audioAmpIsMuted()) + { + bool flashOn = ((ticksGetMillis() % 2000) < 500); + LedWrite(LED_GREEN, 0); + LedWrite(LED_RED, (flashOn ? 1 : 0)); + muteFlashActive = true; + } + else if (muteFlashActive) + { + // Just unmuted: release our forced LED state. + LedWrite(LED_RED, 0); + LedWrite(LED_GREEN, 0); + muteFlashActive = false; + } + } + while(ticksGetMillis() < startTime + 1) // ensure this Task runs at 1ms intervals. Regardless of clock speed. { vTaskDelay(0U); diff --git a/MDUV380_firmware/application/source/user_interface/uiChannelMode.c b/MDUV380_firmware/application/source/user_interface/uiChannelMode.c index a321575..44633a1 100644 --- a/MDUV380_firmware/application/source/user_interface/uiChannelMode.c +++ b/MDUV380_firmware/application/source/user_interface/uiChannelMode.c @@ -1224,6 +1224,15 @@ static void handleEvent(uiEvent_t *ev) return; } + // SK2 short tap toggles audio mute. The DMR TG-set handler above consumes the + // tap (and returns) only while a QSO is on screen, so this won't fire then. + if (BUTTONCHECK_SHORTUP(ev, BUTTON_SK2) && (BUTTONCHECK_DOWN(ev, BUTTON_SK1) == 0) && (monitorModeData.isEnabled == false)) + { + toggleAudioMute(); + uiChannelModeUpdateScreen(0); + return; + } + // Display channel settings (RX/TX/etc) while SK1 is pressed if ((uiDataGlobal.displayChannelSettings == false) && (monitorModeData.isEnabled == false) && BUTTONCHECK_DOWN(ev, BUTTON_SK1)) { diff --git a/MDUV380_firmware/application/source/user_interface/uiUtilities.c b/MDUV380_firmware/application/source/user_interface/uiUtilities.c index 03cd57e..8b7122e 100644 --- a/MDUV380_firmware/application/source/user_interface/uiUtilities.c +++ b/MDUV380_firmware/application/source/user_interface/uiUtilities.c @@ -1852,6 +1852,14 @@ void uiUtilityRenderQSOData(void) displayThemeResetToDefault(); } +// Toggle the audio mute (speaker) state. The header shows a mute icon and the LED +// flashes amber (see the main loop) while muted. +void toggleAudioMute(void) +{ + audioAmpMute(!audioAmpIsMuted()); + headerRowIsDirty = true; // refresh the header so the mute icon appears/clears +} + void uiUtilityRenderHeader(bool isVFODualWatchScanning, bool isVFOSweepScanning, bool forceBatteryDisplay) { const int MODE_TEXT_X_OFFSET = 1; @@ -2176,6 +2184,14 @@ void uiUtilityRenderHeader(bool isVFODualWatchScanning, bool isVFOSweepScanning, bool displayTime = (settingsIsOptionBitSet(BIT_DISPLAY_TIME_IN_HEADER) && (forceBatteryDisplay == false) && (batteryIsLow == false)); #endif #endif + // Mute indicator: bold "M" just left of the battery indicator. + if (audioAmpIsMuted()) + { + displayThemeApply(THEME_ITEM_FG_HEADER_TEXT, THEME_ITEM_BG_HEADER_TEXT); + displayPrintCore((DISPLAY_SIZE_X - 40), DISPLAY_Y_POS_HEADER, "M", FONT_SIZE_1_BOLD, TEXT_ALIGN_LEFT, false); + displayThemeResetToDefault(); + } + // Display battery percentage/voltage bool apoEnabled = (nonVolatileSettings.apo > 0); diff --git a/MDUV380_firmware/application/source/user_interface/uiVFOMode.c b/MDUV380_firmware/application/source/user_interface/uiVFOMode.c index 9c75900..32c0c11 100644 --- a/MDUV380_firmware/application/source/user_interface/uiVFOMode.c +++ b/MDUV380_firmware/application/source/user_interface/uiVFOMode.c @@ -1161,6 +1161,15 @@ static void handleEvent(uiEvent_t *ev) return; } + // SK2 short tap toggles audio mute. The DMR TG-set handler above consumes the + // tap (and returns) only while a QSO is on screen, so this won't fire then. + if (BUTTONCHECK_SHORTUP(ev, BUTTON_SK2) && (BUTTONCHECK_DOWN(ev, BUTTON_SK1) == 0) && (monitorModeData.isEnabled == false)) + { + toggleAudioMute(); + uiVFOModeUpdateScreen(0); + return; + } + if ((uiVFOModeSweepScanning(true) == false) && (monitorModeData.isEnabled == false) && (uiDataGlobal.reverseRepeaterVFO == false) && (BUTTONCHECK_DOWN(ev, BUTTON_SK1) && BUTTONCHECK_DOWN(ev, BUTTON_SK2))) { int prevQSODisp = -1;