mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-12 21:07:19 +00:00
* Screenless Devices want to mute too! * Add logging for actions * Gate to screenless devices only * WisMesh Tag was missing HAS_SCREEN 0 --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
213 lines
6.3 KiB
C++
213 lines
6.3 KiB
C++
#include "buzz.h"
|
|
#include "NodeDB.h"
|
|
#include "configuration.h"
|
|
|
|
#if !defined(ARCH_ESP32) && !defined(ARCH_RP2040) && !defined(ARCH_PORTDUINO)
|
|
#include "Tone.h"
|
|
#endif
|
|
|
|
#if !defined(ARCH_PORTDUINO)
|
|
extern "C" void delay(uint32_t dwMs);
|
|
#endif
|
|
|
|
struct ToneDuration {
|
|
int frequency_khz;
|
|
int duration_ms;
|
|
};
|
|
|
|
// Some common frequencies.
|
|
#define NOTE_SILENT 1
|
|
#define NOTE_C3 131
|
|
#define NOTE_CS3 139
|
|
#define NOTE_D3 147
|
|
#define NOTE_DS3 156
|
|
#define NOTE_E3 165
|
|
#define NOTE_F3 175
|
|
#define NOTE_FS3 185
|
|
#define NOTE_G3 196
|
|
#define NOTE_GS3 208
|
|
#define NOTE_A3 220
|
|
#define NOTE_AS3 233
|
|
#define NOTE_B3 247
|
|
#define NOTE_CS4 277
|
|
#define NOTE_B4 494
|
|
#define NOTE_F5 698
|
|
#define NOTE_G6 1568
|
|
#define NOTE_E7 2637
|
|
|
|
#define NOTE_C4 262
|
|
#define NOTE_E4 330
|
|
#define NOTE_G4 392
|
|
#define NOTE_A4 440
|
|
#define NOTE_C5 523
|
|
#define NOTE_E5 659
|
|
#define NOTE_G5 784
|
|
|
|
const int DURATION_1_16 = 62; // 1/16 note
|
|
const int DURATION_1_8 = 125; // 1/8 note
|
|
const int DURATION_1_4 = 250; // 1/4 note
|
|
const int DURATION_1_2 = 500; // 1/2 note
|
|
const int DURATION_3_4 = 750; // 3/4 note
|
|
const int DURATION_1_1 = 1000; // 1/1 note
|
|
|
|
void playTones(const ToneDuration *tone_durations, int size)
|
|
{
|
|
if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED ||
|
|
config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_NOTIFICATIONS_ONLY) {
|
|
// Buzzer is disabled or not set to system tones
|
|
return;
|
|
}
|
|
#ifdef PIN_BUZZER
|
|
if (!config.device.buzzer_gpio)
|
|
config.device.buzzer_gpio = PIN_BUZZER;
|
|
#endif
|
|
if (config.device.buzzer_gpio) {
|
|
for (int i = 0; i < size; i++) {
|
|
const auto &tone_duration = tone_durations[i];
|
|
tone(config.device.buzzer_gpio, tone_duration.frequency_khz, tone_duration.duration_ms);
|
|
// to distinguish the notes, set a minimum time between them.
|
|
delay(1.3 * tone_duration.duration_ms);
|
|
}
|
|
}
|
|
}
|
|
|
|
void playBeep()
|
|
{
|
|
ToneDuration melody[] = {{NOTE_B3, DURATION_1_16}};
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void playLongBeep()
|
|
{
|
|
ToneDuration melody[] = {{NOTE_B3, DURATION_1_1}};
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void playGPSEnableBeep()
|
|
{
|
|
#if defined(R1_NEO) || defined(MUZI_BASE)
|
|
ToneDuration melody[] = {
|
|
{NOTE_F5, DURATION_1_2}, {NOTE_G6, DURATION_1_8}, {NOTE_E7, DURATION_1_4}, {NOTE_SILENT, DURATION_1_2}};
|
|
#else
|
|
ToneDuration melody[] = {{NOTE_C3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_CS4, DURATION_1_4}};
|
|
#endif
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void playGPSDisableBeep()
|
|
{
|
|
#if defined(R1_NEO) || defined(MUZI_BASE)
|
|
ToneDuration melody[] = {{NOTE_B4, DURATION_1_16}, {NOTE_B4, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8},
|
|
{NOTE_F3, DURATION_1_16}, {NOTE_F3, DURATION_1_16}, {NOTE_SILENT, DURATION_1_8},
|
|
{NOTE_C3, DURATION_1_1}, {NOTE_SILENT, DURATION_1_1}};
|
|
#else
|
|
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}, {NOTE_C3, DURATION_1_4}};
|
|
#endif
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void playStartMelody()
|
|
{
|
|
ToneDuration melody[] = {{NOTE_FS3, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_CS4, DURATION_1_4}};
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void playShutdownMelody()
|
|
{
|
|
ToneDuration melody[] = {{NOTE_CS4, DURATION_1_8}, {NOTE_AS3, DURATION_1_8}, {NOTE_FS3, DURATION_1_4}};
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void playChirp()
|
|
{
|
|
// A short, friendly "chirp" sound for key presses
|
|
ToneDuration melody[] = {{NOTE_AS3, 20}}; // Short AS3 note
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void playClick()
|
|
{
|
|
// A very short "click" sound with minimum delay; ideal for rotary encoder events
|
|
ToneDuration melody[] = {{NOTE_AS3, 1}}; // Very Short AS3
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void playBoop()
|
|
{
|
|
// A short, friendly "boop" sound for button presses
|
|
ToneDuration melody[] = {{NOTE_A3, 50}}; // Very short A3 note
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void playLongPressLeadUp()
|
|
{
|
|
// An ascending lead-up sequence for long press - builds anticipation
|
|
ToneDuration melody[] = {
|
|
{NOTE_C3, 100}, // Start low
|
|
{NOTE_E3, 100}, // Step up
|
|
{NOTE_G3, 100}, // Keep climbing
|
|
{NOTE_B3, 150} // Peak with longer note for emphasis
|
|
};
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
// Static state for progressive lead-up notes
|
|
static int leadUpNoteIndex = 0;
|
|
static const ToneDuration leadUpNotes[] = {
|
|
{NOTE_C3, 100}, // Start low
|
|
{NOTE_E3, 100}, // Step up
|
|
{NOTE_G3, 100}, // Keep climbing
|
|
{NOTE_B3, 150} // Peak with longer note for emphasis
|
|
};
|
|
static const int leadUpNotesCount = sizeof(leadUpNotes) / sizeof(ToneDuration);
|
|
|
|
bool playNextLeadUpNote()
|
|
{
|
|
if (leadUpNoteIndex >= leadUpNotesCount) {
|
|
return false; // All notes have been played
|
|
}
|
|
|
|
// Use playTones to handle buzzer logic consistently
|
|
const auto ¬e = leadUpNotes[leadUpNoteIndex];
|
|
playTones(¬e, 1); // Play single note using existing playTones function
|
|
|
|
leadUpNoteIndex++;
|
|
|
|
if (leadUpNoteIndex >= leadUpNotesCount) {
|
|
return false; // this was the final note
|
|
}
|
|
return true; // Note was played (playTones handles buzzer availability internally)
|
|
}
|
|
|
|
void resetLeadUpSequence()
|
|
{
|
|
leadUpNoteIndex = 0;
|
|
}
|
|
|
|
void playComboTune()
|
|
{
|
|
// Quick high-pitched notes with trills
|
|
ToneDuration melody[] = {
|
|
{NOTE_G3, 80}, // Quick chirp
|
|
{NOTE_B3, 60}, // Higher chirp
|
|
{NOTE_CS4, 80}, // Even higher
|
|
{NOTE_G3, 60}, // Quick trill down
|
|
{NOTE_CS4, 60}, // Quick trill up
|
|
{NOTE_B3, 120} // Ending chirp
|
|
};
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void play4ClickDown()
|
|
{
|
|
ToneDuration melody[] = {{NOTE_G5, 55}, {NOTE_E5, 55}, {NOTE_C5, 60}, {NOTE_A4, 55}, {NOTE_G4, 55},
|
|
{NOTE_E4, 65}, {NOTE_C4, 80}, {NOTE_G3, 120}, {NOTE_E3, 160}, {NOTE_SILENT, 120}};
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
}
|
|
|
|
void play4ClickUp()
|
|
{
|
|
// Quick high-pitched notes with trills
|
|
ToneDuration melody[] = {{NOTE_F5, 50}, {NOTE_G6, 45}, {NOTE_E7, 60}};
|
|
playTones(melody, sizeof(melody) / sizeof(ToneDuration));
|
|
} |