Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion protobufs
71 changes: 71 additions & 0 deletions src/buzz/FindNodeBuzzer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "FindNodeBuzzer.h"
#include "NodeDB.h"
#include "buzz.h"
#include "configuration.h"
#include "mesh/Throttle.h"
#include <Arduino.h>
#include <limits.h>

namespace
{
constexpr uint32_t FIND_NODE_REPEAT_INTERVAL_MS = 2000;
}

FindNodeBuzzer *findNodeBuzzer;

FindNodeBuzzer::FindNodeBuzzer() : concurrency::OSThread("FindNodeBuzzer", INT32_MAX) {}

FindNodeBuzzer::Result FindNodeBuzzer::start()
{
if (!hasFindNodeBuzzer()) {
return Result::NoBuzzer;
}

if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED) {
return Result::BuzzerDisabled;
}

startMsec = millis();
durationMsec = DEFAULT_DURATION_SECONDS * 1000U;

if (!playFindNodeBuzzer()) {
stop();
return Result::NoBuzzer;
}

setIntervalFromNow(FIND_NODE_REPEAT_INTERVAL_MS);
return Result::Started;
}

FindNodeBuzzer::Result FindNodeBuzzer::stop()
{
startMsec = 0;
durationMsec = 0;
setIntervalFromNow(INT32_MAX);
return Result::Stopped;
}

bool FindNodeBuzzer::isActive() const
{
return durationMsec != 0 && Throttle::isWithinTimespanMs(startMsec, durationMsec);
}

int32_t FindNodeBuzzer::runOnce()
{
if (!isActive()) {
stop();
return INT32_MAX;
}

if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED || !playFindNodeBuzzer()) {
stop();
return INT32_MAX;
}

if (!isActive()) {
stop();
return INT32_MAX;
}

return FIND_NODE_REPEAT_INTERVAL_MS;
}
26 changes: 26 additions & 0 deletions src/buzz/FindNodeBuzzer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include "concurrency/OSThread.h"
#include <stdint.h>

class FindNodeBuzzer : private concurrency::OSThread
{
public:
enum class Result : uint8_t { Started, Stopped, NoBuzzer, BuzzerDisabled };

static constexpr uint32_t DEFAULT_DURATION_SECONDS = 5 * 60;

FindNodeBuzzer();

Result start();
Result stop();
bool isActive() const;

private:
uint32_t startMsec = 0;
uint32_t durationMsec = 0;

int32_t runOnce() override;
};

extern FindNodeBuzzer *findNodeBuzzer;
83 changes: 82 additions & 1 deletion src/buzz/buzz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,29 @@
extern "C" void delay(uint32_t dwMs);
#endif

namespace
{
uint32_t getPwmBuzzerGpio()
{
if (config.device.buzzer_gpio) {
return config.device.buzzer_gpio;
}
#if defined(PIN_BUZZER)
return PIN_BUZZER;
#else
return 0;
#endif
}

void enableBuzzerPower()
{
#ifdef BUZZER_EN_PIN
pinMode(BUZZER_EN_PIN, OUTPUT);
digitalWrite(BUZZER_EN_PIN, HIGH);
#endif
}
} // namespace

struct ToneDuration {
int frequency_khz;
int duration_ms;
Expand Down Expand Up @@ -117,6 +140,7 @@ void playTones(const ToneDuration *tone_durations, int size)
config.device.buzzer_gpio = PIN_BUZZER;
#endif
if (config.device.buzzer_gpio) {
enableBuzzerPower();
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);
Expand All @@ -126,6 +150,63 @@ void playTones(const ToneDuration *tone_durations, int size)
}
}

bool hasFindNodeBuzzer()
{
#ifdef HAS_I2S
if (moduleConfig.external_notification.use_i2s_as_buzzer && audioThread) {
return true;
}
#endif
#if defined(PIN_BUZZER)
return true;
#endif
return getPwmBuzzerGpio() ||
(moduleConfig.external_notification.output_buzzer && !moduleConfig.external_notification.use_pwm);
}

bool playFindNodeBuzzer()
{
if (config.device.buzzer_mode == meshtastic_Config_DeviceConfig_BuzzerMode_DISABLED) {
return false;
}

#ifdef HAS_I2S
if (moduleConfig.external_notification.use_i2s_as_buzzer && audioThread) {
static constexpr const char *findNodeRtttl = "find:d=16,o=5,b=220:c6,p,c6,p,g6";
if (!audioThread->isPlaying()) {
audioThread->beginRttl(findNodeRtttl, strlen(findNodeRtttl));
}
return true;
}
#endif

const uint32_t buzzerGpio = getPwmBuzzerGpio();
if (buzzerGpio) {
enableBuzzerPower();
const ToneDuration melody[] = {{NOTE_C5, DURATION_1_8}, {NOTE_C5, DURATION_1_8}, {NOTE_G5, DURATION_1_8}};
for (const auto &tone_duration : melody) {
tone(buzzerGpio, tone_duration.frequency_khz, tone_duration.duration_ms);
delay(1.3 * tone_duration.duration_ms);
}
return true;
}

if (moduleConfig.external_notification.output_buzzer && !moduleConfig.external_notification.use_pwm) {
const uint32_t buzzerPin = moduleConfig.external_notification.output_buzzer;
enableBuzzerPower();
pinMode(buzzerPin, OUTPUT);
for (int i = 0; i < 3; i++) {
digitalWrite(buzzerPin, HIGH);
delay(DURATION_1_8);
digitalWrite(buzzerPin, LOW);
delay(DURATION_1_8);
}
return true;
}

return false;
}

void playBeep()
{
ToneDuration melody[] = {{NOTE_B3, DURATION_1_16}};
Expand Down Expand Up @@ -265,4 +346,4 @@ 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));
}
}
4 changes: 3 additions & 1 deletion src/buzz/buzz.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ void playChirp();
void playClick();
void playLongPressLeadUp();
bool playNextLeadUpNote(); // Play the next note in the lead-up sequence
void resetLeadUpSequence(); // Reset the lead-up sequence to start from beginning
void resetLeadUpSequence(); // Reset the lead-up sequence to start from beginning
bool hasFindNodeBuzzer();
bool playFindNodeBuzzer();
11 changes: 11 additions & 0 deletions src/input/InputBroker.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#include "InputBroker.h"
#include "PowerFSM.h" // needed for event trigger
#include "buzz/FindNodeBuzzer.h"
#include "configuration.h"
#include "graphics/Screen.h"
#include "modules/ExternalNotificationModule.h"
#include <cstring>
#ifdef MESHTASTIC_LOCKDOWN
#include "security/LockdownDisplay.h"
#endif
Expand Down Expand Up @@ -111,6 +113,13 @@ int InputBroker::handleInputEvent(const InputEvent *event)
#endif
powerFSM.trigger(EVENT_INPUT);

const bool isLocalInput =
event && event->inputEvent != INPUT_BROKER_NONE && (!event->source || strcmp(event->source, "admin") != 0);
if (isLocalInput && findNodeBuzzer && findNodeBuzzer->isActive()) {
findNodeBuzzer->stop();
LOG_INFO("Find-node buzzer stopped by local input");
}

if (event && event->inputEvent != INPUT_BROKER_NONE && externalNotificationModule &&
moduleConfig.external_notification.enabled && externalNotificationModule->nagging()) {
externalNotificationModule->stopNow();
Expand Down Expand Up @@ -391,8 +400,10 @@ void InputBroker::Init()
rotaryEncoderInterruptImpl1 = nullptr;
}
#endif
#if !MESHTASTIC_EXCLUDE_I2C
cardKbI2cImpl = new CardKbI2cImpl();
cardKbI2cImpl->init();
#endif
#if defined(M5STACK_UNITC6L)
i2cButton = new i2cButtonThread("i2cButtonThread");
#endif
Expand Down
1 change: 1 addition & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,7 @@ extern meshtastic_DeviceMetadata getDeviceMetadata()
deviceMetadata.position_flags = config.position.position_flags;
deviceMetadata.hw_model = HW_VENDOR;
deviceMetadata.hasRemoteHardware = moduleConfig.remote_hardware.enabled;
deviceMetadata.hasBuzzer = hasFindNodeBuzzer();
deviceMetadata.excluded_modules = meshtastic_ExcludedModules_EXCLUDED_NONE;
#if MESHTASTIC_EXCLUDE_REMOTEHARDWARE
deviceMetadata.excluded_modules |= meshtastic_ExcludedModules_REMOTEHARDWARE_CONFIG;
Expand Down
10 changes: 9 additions & 1 deletion src/mesh/generated/meshtastic/admin.pb.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.9.1 */
/* Generated by nanopb-0.4.9 */

#include "meshtastic/admin.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
Expand All @@ -15,6 +15,12 @@ PB_BIND(meshtastic_AdminMessage_InputEvent, meshtastic_AdminMessage_InputEvent,
PB_BIND(meshtastic_AdminMessage_OTAEvent, meshtastic_AdminMessage_OTAEvent, AUTO)


PB_BIND(meshtastic_AdminMessage_FindNodeRequest, meshtastic_AdminMessage_FindNodeRequest, AUTO)


PB_BIND(meshtastic_AdminMessage_FindNodeResponse, meshtastic_AdminMessage_FindNodeResponse, AUTO)


PB_BIND(meshtastic_LockdownAuth, meshtastic_LockdownAuth, AUTO)


Expand Down Expand Up @@ -56,3 +62,5 @@ PB_BIND(meshtastic_SHTXX_config, meshtastic_SHTXX_config, AUTO)





Loading