mirror of
https://github.com/pelgraine/Meck.git
synced 2026-08-09 02:02:44 +02:00
Max - New buzzer(vibrate) option in custom channel notification preferences - silent buzz notification.
This commit is contained in:
@@ -12,6 +12,10 @@
|
||||
#include "ModemManager.h" // Serial CLI modem commands
|
||||
#endif
|
||||
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
#include "DRV2605Haptic.h" // TEMP: inline haptic driver for the 'buzz' CLI test command
|
||||
#endif
|
||||
|
||||
#define CMD_APP_START 1
|
||||
#define CMD_SEND_TXT_MSG 2
|
||||
#define CMD_SEND_CHANNEL_TXT_MSG 3
|
||||
@@ -3506,7 +3510,27 @@ void MyMesh::checkCLIRescueCmd() {
|
||||
|
||||
}
|
||||
|
||||
} else if (strcmp(cli_command, "reboot") == 0) {
|
||||
}
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
else if (strcmp(cli_command, "buzz") == 0) {
|
||||
// TEMP: fire the DRV2605 haptic motor once to confirm it works.
|
||||
// Lazy-inits the driver (and motor power rail) on first invocation.
|
||||
static DRV2605Haptic haptic;
|
||||
static bool haptic_ready = false;
|
||||
if (!haptic_ready) {
|
||||
board.motorEnable();
|
||||
delay(10); // let the motor rail settle before I2C
|
||||
haptic_ready = haptic.begin();
|
||||
}
|
||||
if (haptic_ready) {
|
||||
haptic.buzz(1);
|
||||
Serial.println(" > buzz");
|
||||
} else {
|
||||
Serial.println(" > buzz: DRV2605 not found");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if (strcmp(cli_command, "reboot") == 0) {
|
||||
board.reboot(); // doesn't return
|
||||
} else {
|
||||
Serial.println(" Error: unknown command (try 'help')");
|
||||
|
||||
@@ -38,6 +38,10 @@
|
||||
#endif
|
||||
|
||||
#define NOTIF_SOUND_NAME_MAX 32
|
||||
// Reserved one-byte sentinel stored in a channel's name slot to mean
|
||||
// "Buzzer (vibrate)" instead of a sound filename. 0x01 can never be the first
|
||||
// byte of a real filename, so it is unambiguous and needs no config change.
|
||||
#define NOTIF_VIBRATE_MARKER "\x01"
|
||||
#define NOTIF_SOUND_SLOTS (MAX_GROUP_CHANNELS + 1) // +1 for DMs
|
||||
#define NOTIF_SOUND_CONFIG_PATH "/meshcore/notif_sounds.cfg"
|
||||
#define NOTIF_SOUND_MAGIC 0x4E534E44 // "NSND"
|
||||
@@ -100,6 +104,18 @@ public:
|
||||
setSoundForChannel(channel_idx, nullptr);
|
||||
}
|
||||
|
||||
// --- Vibrate (haptic) selection ---
|
||||
// "Buzzer (vibrate)" is stored as a reserved marker in the channel's name
|
||||
// slot, so it persists through the existing config save/load unchanged.
|
||||
bool isVibrateForChannel(uint8_t channel_idx) const {
|
||||
const char* s = getSoundForChannel(channel_idx);
|
||||
return s && (uint8_t)s[0] == (uint8_t)NOTIF_VIBRATE_MARKER[0];
|
||||
}
|
||||
|
||||
void setVibrateForChannel(uint8_t channel_idx) {
|
||||
setSoundForChannel(channel_idx, NOTIF_VIBRATE_MARKER);
|
||||
}
|
||||
|
||||
// --- Sound file scanning ---
|
||||
|
||||
void scanSoundFiles() {
|
||||
|
||||
@@ -2345,9 +2345,14 @@ public:
|
||||
int maxVisible = (listBot - listTop) / lineH;
|
||||
if (maxVisible < 3) maxVisible = 3;
|
||||
|
||||
// Total items: 1 ("Default") + sound file count
|
||||
// Total items: "Default" (+ "Buzzer (vibrate)" on MAX) + sound file count
|
||||
const auto& files = notifSounds.getSoundFiles();
|
||||
int totalItems = 1 + (int)files.size();
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
const int kFileBase = 2; // 0=Default, 1=Buzzer(vibrate), 2+=files
|
||||
#else
|
||||
const int kFileBase = 1; // 0=Default, 1+=files
|
||||
#endif
|
||||
int totalItems = kFileBase + (int)files.size();
|
||||
|
||||
// Centre scroll on selection
|
||||
_notifSoundScroll = max(0, min(_notifSoundSelected - maxVisible / 2,
|
||||
@@ -2370,9 +2375,13 @@ public:
|
||||
display.setCursor(bx + 6, sy);
|
||||
if (i == 0) {
|
||||
display.print("Default (silent)");
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
} else if (i == 1) {
|
||||
display.print("Buzzer (vibrate)");
|
||||
#endif
|
||||
} else {
|
||||
// Show filename without extension
|
||||
String displayName = files[i - 1];
|
||||
String displayName = files[i - kFileBase];
|
||||
int dot = displayName.lastIndexOf('.');
|
||||
if (dot > 0) displayName = displayName.substring(0, dot);
|
||||
if (displayName.length() > 28) displayName = displayName.substring(0, 28);
|
||||
@@ -2954,7 +2963,12 @@ public:
|
||||
#if defined(MECK_AUDIO_VARIANT) || defined(HAS_4G_MODEM)
|
||||
if (_editMode == EDIT_NOTIF_SOUND) {
|
||||
const auto& files = notifSounds.getSoundFiles();
|
||||
int totalItems = 1 + (int)files.size(); // 0=Default, rest=files
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
const int kFileBase = 2; // 0=Default, 1=Buzzer(vibrate), 2+=files
|
||||
#else
|
||||
const int kFileBase = 1; // 0=Default, 1+=files
|
||||
#endif
|
||||
int totalItems = kFileBase + (int)files.size();
|
||||
|
||||
if (c == 'w' || c == 'W' || c == 0xF2 || c == KEY_UP) {
|
||||
if (_notifSoundSelected > 0) _notifSoundSelected--;
|
||||
@@ -2965,11 +2979,15 @@ public:
|
||||
return true;
|
||||
}
|
||||
if (c == '\r' || c == 13) {
|
||||
// Select: 0 = clear (default silent), 1+ = file
|
||||
// Select: 0 = clear (default silent), [MAX: 1 = vibrate], rest = file
|
||||
if (_notifSoundSelected == 0) {
|
||||
notifSounds.clearSoundForChannel(_notifSoundChannel);
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
} else if (_notifSoundSelected == 1) {
|
||||
notifSounds.setVibrateForChannel(_notifSoundChannel);
|
||||
#endif
|
||||
} else {
|
||||
int fileIdx = _notifSoundSelected - 1;
|
||||
int fileIdx = _notifSoundSelected - kFileBase;
|
||||
if (fileIdx >= 0 && fileIdx < (int)files.size()) {
|
||||
notifSounds.setSoundForChannel(_notifSoundChannel, files[fileIdx].c_str());
|
||||
}
|
||||
@@ -3904,14 +3922,26 @@ public:
|
||||
notifSounds.scanSoundFiles();
|
||||
_notifSoundSelected = 0; // 0 = "Default (silent)"
|
||||
_notifSoundScroll = 0;
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
const int kFileBase = 2; // 0=Default, 1=Buzzer(vibrate), 2+=files
|
||||
#else
|
||||
const int kFileBase = 1; // 0=Default, 1+=files
|
||||
#endif
|
||||
// Pre-select current assignment
|
||||
const char* current = notifSounds.getSoundForChannel(_notifSoundChannel);
|
||||
if (current && current[0] != '\0') {
|
||||
const auto& files = notifSounds.getSoundFiles();
|
||||
for (int i = 0; i < (int)files.size(); i++) {
|
||||
if (files[i] == String(current)) {
|
||||
_notifSoundSelected = i + 1; // +1 because 0 is "Default"
|
||||
break;
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
if (notifSounds.isVibrateForChannel(_notifSoundChannel)) {
|
||||
_notifSoundSelected = 1; // 1 = "Buzzer (vibrate)"
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
const char* current = notifSounds.getSoundForChannel(_notifSoundChannel);
|
||||
if (current && current[0] != '\0') {
|
||||
const auto& files = notifSounds.getSoundFiles();
|
||||
for (int i = 0; i < (int)files.size(); i++) {
|
||||
if (files[i] == String(current)) {
|
||||
_notifSoundSelected = i + kFileBase;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
#include "MapScreen.h"
|
||||
#endif
|
||||
#include "target.h"
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
#include "DRV2605Haptic.h" // haptic motor for "Buzzer (vibrate)" channels
|
||||
#endif
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro) || defined(MECK_AUDIO_VARIANT)
|
||||
#include "HomeIcons.h"
|
||||
#endif
|
||||
@@ -1645,6 +1648,21 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i
|
||||
// Set the flag for notify() which is called immediately after newMsg().
|
||||
// If a custom notification tone is assigned and notifications are active,
|
||||
// request MP3 playback and suppress the RTTTL buzzer so they don't overlap.
|
||||
#if defined(LilyGo_TDeck_Pro_Max)
|
||||
// Channel set to "Buzzer (vibrate)": pulse the motor instead of a tone.
|
||||
if (!suppressNotif && notifSounds.isVibrateForChannel(channel_idx)) {
|
||||
static DRV2605Haptic s_haptic;
|
||||
static bool s_hapticReady = false;
|
||||
if (!s_hapticReady) {
|
||||
board.motorEnable();
|
||||
delay(10); // let the motor rail settle before I2C
|
||||
s_hapticReady = s_haptic.begin();
|
||||
}
|
||||
if (s_hapticReady) s_haptic.buzz(1);
|
||||
s_lastMsgSuppressed = true; // suppress the default RTTTL buzzer
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#ifdef MECK_AUDIO_VARIANT
|
||||
if (!suppressNotif) {
|
||||
const char* customSound = notifSounds.getSoundForChannel(channel_idx);
|
||||
@@ -1677,7 +1695,8 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i
|
||||
#else
|
||||
s_lastMsgSuppressed = suppressNotif;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// Add to channel history screen with channel index, path data, and SNR
|
||||
// For DMs (channel_idx == 0xFF):
|
||||
// - Regular DMs: prefix text with sender name ("NodeName: hello")
|
||||
|
||||
Reference in New Issue
Block a user