mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-26 14:58:12 +00:00
Add ringtone editor, buzzer volume, unread fixes and buzzer notification fixes
- RingtoneEditorScreen: 32-note step sequencer accessible via new Tools page on home screen. U/D=pitch, ENTER=octave cycle, L/R=cursor, MENU=options (play, duration, BPM, insert/delete, save). Notes stored packed in NodePrefs and persisted via DataStore. - ToolsScreen: new "Tools" home page entry launching the editor - Buzzer volume: 0-4 bar control in Settings > Sound, persisted to prefs - Buzzer notifications: fixed notify() firing inside wrong serial-guard branch and before addChannelMsg set the channel index - Unread counter: fixed newest-first index direction; watermark formula now correct - OLED brightness: wider range via pre-charge register 0xD9 combined with contrast Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -246,6 +246,12 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no
|
||||
file.read((uint8_t *)&_prefs.room_fav_only, sizeof(_prefs.room_fav_only)); // 1561
|
||||
if (file.available()) {
|
||||
file.read((uint8_t *)&_prefs.buzzer_volume, sizeof(_prefs.buzzer_volume)); // 1562
|
||||
if (file.available()) {
|
||||
file.read((uint8_t *)&_prefs.ringtone_bpm_idx, sizeof(_prefs.ringtone_bpm_idx)); // 1563
|
||||
file.read((uint8_t *)&_prefs.ringtone_len, sizeof(_prefs.ringtone_len)); // 1564
|
||||
if (_prefs.ringtone_len > 32) _prefs.ringtone_len = 0;
|
||||
file.read((uint8_t *)_prefs.ringtone_notes, sizeof(_prefs.ringtone_notes)); // 1565
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +304,10 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_
|
||||
file.write((uint8_t *)&_prefs.ch_notif_muted, sizeof(_prefs.ch_notif_muted)); // 1552
|
||||
file.write((uint8_t *)&_prefs.dm_show_all, sizeof(_prefs.dm_show_all)); // 1560
|
||||
file.write((uint8_t *)&_prefs.room_fav_only, sizeof(_prefs.room_fav_only)); // 1561
|
||||
file.write((uint8_t *)&_prefs.buzzer_volume, sizeof(_prefs.buzzer_volume)); // 1562
|
||||
file.write((uint8_t *)&_prefs.buzzer_volume, sizeof(_prefs.buzzer_volume)); // 1562
|
||||
file.write((uint8_t *)&_prefs.ringtone_bpm_idx, sizeof(_prefs.ringtone_bpm_idx)); // 1563
|
||||
file.write((uint8_t *)&_prefs.ringtone_len, sizeof(_prefs.ringtone_len)); // 1564
|
||||
file.write((uint8_t *)_prefs.ringtone_notes, sizeof(_prefs.ringtone_notes)); // 1565
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
@@ -865,6 +865,8 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
_prefs.gps_interval = 0; // No automatic GPS updates by default
|
||||
_prefs.display_brightness = 2; // medium brightness by default
|
||||
_prefs.buzzer_volume = 4; // max volume by default
|
||||
_prefs.ringtone_bpm_idx = 2; // 120 bpm default
|
||||
_prefs.ringtone_len = 0; // no custom ringtone by default
|
||||
_prefs.auto_off_secs = 15; // 15 seconds auto-off by default
|
||||
_prefs.tz_offset_hours = 0; // UTC by default
|
||||
_prefs.low_batt_mv = 3400; // auto-shutdown at 3.4V by default
|
||||
|
||||
@@ -45,4 +45,7 @@ struct NodePrefs { // persisted to file
|
||||
uint64_t ch_notif_muted; // bitmask: bit i = channel i muted (only if override bit set)
|
||||
uint8_t dm_show_all; // 0=favourites only (default), 1=all chat contacts
|
||||
uint8_t room_fav_only; // 0=all room servers (default), 1=favourites only
|
||||
uint8_t ringtone_bpm_idx; // index into {60,90,120,150,180}
|
||||
uint8_t ringtone_len; // number of notes in custom ringtone (0 = use default)
|
||||
uint8_t ringtone_notes[32]; // packed: bits0-2=pitch, bits3-4=octave-4, bits5-6=dur_idx
|
||||
};
|
||||
@@ -1681,6 +1681,343 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// ── RingtoneEditorScreen ──────────────────────────────────────────────────────
|
||||
class RingtoneEditorScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
NodePrefs* _prefs;
|
||||
|
||||
static const int MAX_NOTES = 32;
|
||||
static const int VISIBLE_NOTES = 7;
|
||||
static const int CELL_W = 18;
|
||||
static const int NOTES_Y = 12;
|
||||
static const int CELL_H = 14;
|
||||
static const int MENU_VISIBLE = 5;
|
||||
|
||||
static const uint16_t BPM_OPTS[5];
|
||||
static const uint8_t DUR_VALS[4];
|
||||
static const char* DUR_LABELS[4];
|
||||
static const char PITCH_NAMES[8]; // lowercase rtttl names
|
||||
|
||||
uint8_t _notes[MAX_NOTES];
|
||||
uint8_t _len;
|
||||
uint8_t _bpm_idx;
|
||||
int _cursor;
|
||||
int _scroll;
|
||||
bool _menu_open;
|
||||
int _menu_sel;
|
||||
int _menu_scroll;
|
||||
|
||||
enum MenuOpt {
|
||||
M_PLAY_STOP, M_DURATION, M_BPM_UP, M_BPM_DOWN,
|
||||
M_INSERT, M_DELETE, M_SAVE, M_DISCARD, M_COUNT
|
||||
};
|
||||
static const char* MENU_LABELS[M_COUNT];
|
||||
|
||||
static uint8_t notePitch(uint8_t b) { return b & 0x07; }
|
||||
static uint8_t noteOctave(uint8_t b) { return ((b >> 3) & 0x03) + 4; }
|
||||
static uint8_t noteDurIdx(uint8_t b) { return (b >> 5) & 0x03; }
|
||||
static uint8_t packNote(uint8_t pitch, uint8_t octave, uint8_t dur_idx) {
|
||||
return (pitch & 0x07) | (((octave - 4) & 0x03) << 3) | ((dur_idx & 0x03) << 5);
|
||||
}
|
||||
|
||||
void clampScroll() {
|
||||
if (_cursor < _scroll) _scroll = _cursor;
|
||||
if (_cursor >= _scroll + VISIBLE_NOTES) _scroll = _cursor - VISIBLE_NOTES + 1;
|
||||
if (_scroll < 0) _scroll = 0;
|
||||
}
|
||||
|
||||
void buildRTTTL(char* buf, int buf_len) {
|
||||
uint16_t bpm = BPM_OPTS[_bpm_idx < 5 ? _bpm_idx : 2];
|
||||
int pos = snprintf(buf, buf_len, "Ring:d=8,o=5,b=%u:", bpm);
|
||||
for (int i = 0; i < _len && pos < buf_len - 8; i++) {
|
||||
if (i > 0 && pos < buf_len - 1) buf[pos++] = ',';
|
||||
uint8_t pitch = notePitch(_notes[i]);
|
||||
uint8_t octave = noteOctave(_notes[i]);
|
||||
uint8_t dur_val = DUR_VALS[noteDurIdx(_notes[i])];
|
||||
if (pitch == 0)
|
||||
pos += snprintf(buf + pos, buf_len - pos, "%dp", dur_val);
|
||||
else
|
||||
pos += snprintf(buf + pos, buf_len - pos, "%d%c%d", dur_val, PITCH_NAMES[pitch], octave);
|
||||
}
|
||||
if (pos < buf_len) buf[pos] = '\0';
|
||||
}
|
||||
|
||||
public:
|
||||
RingtoneEditorScreen(UITask* task, NodePrefs* prefs) : _task(task), _prefs(prefs) {}
|
||||
|
||||
void enter() {
|
||||
_bpm_idx = (_prefs && _prefs->ringtone_bpm_idx < 5) ? _prefs->ringtone_bpm_idx : 2;
|
||||
_len = (_prefs && _prefs->ringtone_len <= (uint8_t)MAX_NOTES) ? _prefs->ringtone_len : 0;
|
||||
if (_prefs) memcpy(_notes, _prefs->ringtone_notes, sizeof(_notes));
|
||||
_cursor = 0;
|
||||
_scroll = 0;
|
||||
_menu_open = false;
|
||||
_menu_sel = 0;
|
||||
_menu_scroll = 0;
|
||||
}
|
||||
|
||||
int render(DisplayDriver& display) override {
|
||||
display.setTextSize(1);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
char hdr[32];
|
||||
snprintf(hdr, sizeof(hdr), "BPM:%u %d/%d", BPM_OPTS[_bpm_idx], _len, MAX_NOTES);
|
||||
display.setCursor(0, 0);
|
||||
display.print(hdr);
|
||||
display.fillRect(0, 10, display.width(), 1);
|
||||
|
||||
for (int i = 0; i < VISIBLE_NOTES; i++) {
|
||||
int ni = _scroll + i;
|
||||
int cx = i * CELL_W;
|
||||
bool sel = (ni == _cursor);
|
||||
if (ni < _len) {
|
||||
uint8_t pitch = notePitch(_notes[ni]);
|
||||
uint8_t octave = noteOctave(_notes[ni]);
|
||||
char label[3];
|
||||
if (pitch == 0) {
|
||||
label[0] = '-'; label[1] = '-'; label[2] = '\0';
|
||||
} else {
|
||||
label[0] = PITCH_NAMES[pitch] - 32; // uppercase
|
||||
label[1] = '0' + octave;
|
||||
label[2] = '\0';
|
||||
}
|
||||
if (sel) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(cx, NOTES_Y, CELL_W - 1, CELL_H);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
} else {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(cx, NOTES_Y, CELL_W - 1, CELL_H);
|
||||
}
|
||||
display.setCursor(cx + 3, NOTES_Y + 3);
|
||||
display.print(label);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
} else if (ni == _len && _len < MAX_NOTES) {
|
||||
if (sel) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(cx, NOTES_Y, CELL_W - 1, CELL_H);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
} else {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
display.setCursor(cx + 6, NOTES_Y + 3);
|
||||
display.print("+");
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
int info_y = NOTES_Y + CELL_H + 2;
|
||||
if (_scroll > 0) { display.setCursor(0, info_y); display.print("<"); }
|
||||
if (_scroll + VISIBLE_NOTES <= _len) { display.setCursor(display.width() - 6, info_y); display.print(">"); }
|
||||
if (_cursor < _len) {
|
||||
char info[24];
|
||||
snprintf(info, sizeof(info), "oct:%d dur:%s",
|
||||
noteOctave(_notes[_cursor]), DUR_LABELS[noteDurIdx(_notes[_cursor])]);
|
||||
display.setCursor(10, info_y);
|
||||
display.print(info);
|
||||
} else if (_cursor == _len) {
|
||||
display.setCursor(10, info_y);
|
||||
display.print("U/D to add note");
|
||||
}
|
||||
|
||||
display.setCursor(0, 54);
|
||||
display.print("ENT:oct MENU:opts");
|
||||
|
||||
if (_menu_open) {
|
||||
static const int mw = 100, mh = MENU_VISIBLE * 10 + 4;
|
||||
int mx = (128 - mw) / 2;
|
||||
int my = (64 - mh) / 2;
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
display.fillRect(mx, my, mw, mh);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(mx, my, mw, mh);
|
||||
for (int i = 0; i < MENU_VISIBLE; i++) {
|
||||
int item = _menu_scroll + i;
|
||||
if (item >= M_COUNT) break;
|
||||
int iy = my + 2 + i * 10;
|
||||
bool sel = (item == _menu_sel);
|
||||
if (sel) {
|
||||
display.fillRect(mx + 1, iy - 1, mw - 2, 10);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
}
|
||||
display.setCursor(mx + 4, iy);
|
||||
if (item == M_PLAY_STOP)
|
||||
display.print(_task->isMelodyPlaying() ? "Stop" : "Play");
|
||||
else
|
||||
display.print(MENU_LABELS[item]);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
if (_menu_scroll > 0) { display.setCursor(mx + mw - 7, my + 2); display.print("^"); }
|
||||
if (_menu_scroll + MENU_VISIBLE < M_COUNT) { display.setCursor(mx + mw - 7, my + mh - 10); display.print("v"); }
|
||||
}
|
||||
|
||||
return 200;
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
bool up = (c == KEY_UP);
|
||||
bool down = (c == KEY_DOWN);
|
||||
bool left = (c == KEY_LEFT || c == KEY_PREV);
|
||||
bool right = (c == KEY_RIGHT || c == KEY_NEXT);
|
||||
bool enter = (c == KEY_ENTER);
|
||||
bool menu_key = (c == KEY_CONTEXT_MENU);
|
||||
bool cancel = (c == KEY_CANCEL);
|
||||
|
||||
if (_menu_open) {
|
||||
if (cancel) { _menu_open = false; return true; }
|
||||
if (up && _menu_sel > 0) {
|
||||
_menu_sel--;
|
||||
if (_menu_sel < _menu_scroll) _menu_scroll = _menu_sel;
|
||||
return true;
|
||||
}
|
||||
if (down && _menu_sel < M_COUNT - 1) {
|
||||
_menu_sel++;
|
||||
if (_menu_sel >= _menu_scroll + MENU_VISIBLE) _menu_scroll = _menu_sel - MENU_VISIBLE + 1;
|
||||
return true;
|
||||
}
|
||||
if (!enter) return true;
|
||||
|
||||
_menu_open = false;
|
||||
switch ((MenuOpt)_menu_sel) {
|
||||
case M_PLAY_STOP:
|
||||
if (_task->isMelodyPlaying()) {
|
||||
_task->stopMelody();
|
||||
} else if (_len > 0) {
|
||||
char buf[220];
|
||||
buildRTTTL(buf, sizeof(buf));
|
||||
_task->playMelody(buf);
|
||||
}
|
||||
break;
|
||||
case M_DURATION:
|
||||
if (_cursor < _len) {
|
||||
uint8_t p = notePitch(_notes[_cursor]);
|
||||
uint8_t o = noteOctave(_notes[_cursor]);
|
||||
uint8_t di = (noteDurIdx(_notes[_cursor]) + 1) & 0x03;
|
||||
_notes[_cursor] = packNote(p, o, di);
|
||||
}
|
||||
break;
|
||||
case M_BPM_UP: if (_bpm_idx < 4) _bpm_idx++; break;
|
||||
case M_BPM_DOWN: if (_bpm_idx > 0) _bpm_idx--; break;
|
||||
case M_INSERT:
|
||||
if (_len < MAX_NOTES) {
|
||||
int ins = (_cursor < _len) ? _cursor + 1 : _cursor;
|
||||
for (int i = _len; i > ins; i--) _notes[i] = _notes[i - 1];
|
||||
_notes[ins] = packNote(1, 5, 1);
|
||||
_len++;
|
||||
_cursor = ins;
|
||||
clampScroll();
|
||||
}
|
||||
break;
|
||||
case M_DELETE:
|
||||
if (_len > 0 && _cursor < _len) {
|
||||
for (int i = _cursor; i < _len - 1; i++) _notes[i] = _notes[i + 1];
|
||||
_len--;
|
||||
if (_cursor >= _len && _len > 0) _cursor = _len - 1;
|
||||
else if (_len == 0) _cursor = 0;
|
||||
clampScroll();
|
||||
}
|
||||
break;
|
||||
case M_SAVE:
|
||||
if (_prefs) {
|
||||
_prefs->ringtone_bpm_idx = _bpm_idx;
|
||||
_prefs->ringtone_len = _len;
|
||||
memcpy(_prefs->ringtone_notes, _notes, sizeof(_notes));
|
||||
the_mesh.savePrefs();
|
||||
}
|
||||
_task->stopMelody();
|
||||
_task->gotoHomeScreen();
|
||||
break;
|
||||
case M_DISCARD:
|
||||
_task->stopMelody();
|
||||
_task->gotoHomeScreen();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cancel) { _task->stopMelody(); _task->gotoHomeScreen(); return true; }
|
||||
if (menu_key) { _menu_open = true; _menu_sel = 0; _menu_scroll = 0; return true; }
|
||||
|
||||
if (left && _cursor > 0) { _cursor--; clampScroll(); return true; }
|
||||
if (right) {
|
||||
int max_cur = (_len < MAX_NOTES) ? _len : _len - 1;
|
||||
if (_cursor < max_cur) { _cursor++; clampScroll(); return true; }
|
||||
}
|
||||
|
||||
if ((up || down) && _cursor == _len && _len < MAX_NOTES) {
|
||||
_notes[_len] = packNote(1, 5, 1);
|
||||
_len++;
|
||||
clampScroll();
|
||||
}
|
||||
if ((up || down) && _cursor < _len) {
|
||||
uint8_t p = notePitch(_notes[_cursor]);
|
||||
uint8_t o = noteOctave(_notes[_cursor]);
|
||||
uint8_t di = noteDurIdx(_notes[_cursor]);
|
||||
if (up) p = (p + 1) & 0x07;
|
||||
if (down) p = (p + 7) & 0x07;
|
||||
_notes[_cursor] = packNote(p, o, di);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (enter && _cursor < _len) {
|
||||
uint8_t p = notePitch(_notes[_cursor]);
|
||||
uint8_t o = noteOctave(_notes[_cursor]);
|
||||
uint8_t di = noteDurIdx(_notes[_cursor]);
|
||||
if (p != 0) o = (o < 6) ? o + 1 : 4;
|
||||
_notes[_cursor] = packNote(p, o, di);
|
||||
return true;
|
||||
}
|
||||
if (enter && _cursor == _len && _len < MAX_NOTES) {
|
||||
_notes[_len] = packNote(1, 5, 1);
|
||||
_len++;
|
||||
clampScroll();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const uint16_t RingtoneEditorScreen::BPM_OPTS[5] = { 60, 90, 120, 150, 180 };
|
||||
const uint8_t RingtoneEditorScreen::DUR_VALS[4] = { 4, 8, 16, 32 };
|
||||
const char* RingtoneEditorScreen::DUR_LABELS[4] = { "1/4", "1/8", "1/16", "1/32" };
|
||||
const char RingtoneEditorScreen::PITCH_NAMES[8] = { 'p', 'c', 'd', 'e', 'f', 'g', 'a', 'b' };
|
||||
const char* RingtoneEditorScreen::MENU_LABELS[RingtoneEditorScreen::M_COUNT] = {
|
||||
"Play/Stop", "Duration", "BPM+", "BPM-", "Insert", "Delete", "Save & Exit", "Discard"
|
||||
};
|
||||
|
||||
// ── ToolsScreen ───────────────────────────────────────────────────────────────
|
||||
class ToolsScreen : public UIScreen {
|
||||
UITask* _task;
|
||||
|
||||
public:
|
||||
ToolsScreen(UITask* task) : _task(task) {}
|
||||
|
||||
int render(DisplayDriver& display) override {
|
||||
display.setTextSize(1);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawTextCentered(display.width() / 2, 0, "TOOLS");
|
||||
display.fillRect(0, 10, display.width(), 1);
|
||||
|
||||
display.fillRect(0, 12, display.width(), 11);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
display.setCursor(4, 13);
|
||||
display.print("Ringtone Editor");
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
display.setCursor(0, 54);
|
||||
display.print("ENT:open CANCEL:back");
|
||||
return 500;
|
||||
}
|
||||
|
||||
bool handleInput(char c) override {
|
||||
if (c == KEY_CANCEL || c == KEY_CONTEXT_MENU) { _task->gotoHomeScreen(); return true; }
|
||||
if (c == KEY_ENTER) { _task->gotoRingtoneEditor(); return true; }
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// ── HomeScreen ────────────────────────────────────────────────────────────────
|
||||
class HomeScreen : public UIScreen {
|
||||
enum HomePage {
|
||||
CLOCK,
|
||||
@@ -1695,6 +2032,7 @@ class HomeScreen : public UIScreen {
|
||||
SENSORS,
|
||||
#endif
|
||||
SETTINGS,
|
||||
TOOLS,
|
||||
QUICK_MSG,
|
||||
SHUTDOWN,
|
||||
Count // keep as last
|
||||
@@ -2070,6 +2408,11 @@ public:
|
||||
display.setTextSize(1);
|
||||
display.drawTextCentered(display.width() / 2, 30, "Settings");
|
||||
display.drawTextCentered(display.width() / 2, 46, PRESS_LABEL " to open");
|
||||
} else if (_page == HomePage::TOOLS) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.setTextSize(1);
|
||||
display.drawTextCentered(display.width() / 2, 30, "Tools");
|
||||
display.drawTextCentered(display.width() / 2, 46, PRESS_LABEL " to open");
|
||||
} else if (_page == HomePage::QUICK_MSG) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.setTextSize(1);
|
||||
@@ -2141,6 +2484,10 @@ public:
|
||||
_task->gotoSettingsScreen();
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_ENTER && _page == HomePage::TOOLS) {
|
||||
_task->gotoToolsScreen();
|
||||
return true;
|
||||
}
|
||||
if (c == KEY_ENTER && _page == HomePage::QUICK_MSG) {
|
||||
_task->gotoQuickMsgScreen();
|
||||
return true;
|
||||
@@ -2195,6 +2542,8 @@ void UITask::begin(DisplayDriver* display, SensorManager* sensors, NodePrefs* no
|
||||
home = new HomeScreen(this, &rtc_clock, sensors, node_prefs);
|
||||
settings = new SettingsScreen(this);
|
||||
quick_msg = new QuickMsgScreen(this);
|
||||
tools_screen = new ToolsScreen(this);
|
||||
ringtone_edit = new RingtoneEditorScreen(this, node_prefs);
|
||||
setCurrScreen(splash);
|
||||
|
||||
applyBrightness();
|
||||
@@ -2205,6 +2554,35 @@ void UITask::gotoSettingsScreen() {
|
||||
setCurrScreen(settings);
|
||||
}
|
||||
|
||||
void UITask::gotoToolsScreen() {
|
||||
setCurrScreen(tools_screen);
|
||||
}
|
||||
|
||||
void UITask::gotoRingtoneEditor() {
|
||||
((RingtoneEditorScreen*)ringtone_edit)->enter();
|
||||
setCurrScreen(ringtone_edit);
|
||||
}
|
||||
|
||||
void UITask::playMelody(const char* melody) {
|
||||
#ifdef PIN_BUZZER
|
||||
buzzer.playForced(melody);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UITask::stopMelody() {
|
||||
#ifdef PIN_BUZZER
|
||||
buzzer.stop();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UITask::isMelodyPlaying() {
|
||||
#ifdef PIN_BUZZER
|
||||
return buzzer.isPlaying();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UITask::gotoQuickMsgScreen() {
|
||||
((QuickMsgScreen*)quick_msg)->reset();
|
||||
setCurrScreen(quick_msg);
|
||||
|
||||
@@ -55,6 +55,8 @@ class UITask : public AbstractUITask {
|
||||
UIScreen* home;
|
||||
UIScreen* settings;
|
||||
UIScreen* quick_msg;
|
||||
UIScreen* tools_screen;
|
||||
UIScreen* ringtone_edit;
|
||||
UIScreen* curr;
|
||||
|
||||
void userLedHandler();
|
||||
@@ -84,6 +86,11 @@ public:
|
||||
void gotoHomeScreen() { setCurrScreen(home); }
|
||||
void gotoSettingsScreen();
|
||||
void gotoQuickMsgScreen();
|
||||
void gotoToolsScreen();
|
||||
void gotoRingtoneEditor();
|
||||
void playMelody(const char* melody);
|
||||
void stopMelody();
|
||||
bool isMelodyPlaying();
|
||||
void showAlert(const char* text, int duration_millis);
|
||||
void addChannelMsg(uint8_t channel_idx, const char* text) override;
|
||||
int getMsgCount() const { return _msgcount; }
|
||||
|
||||
@@ -44,6 +44,10 @@ void genericBuzzer::setVolume(uint8_t level) {
|
||||
if (isPlaying()) applyVolume();
|
||||
}
|
||||
|
||||
void genericBuzzer::stop() {
|
||||
rtttl::stop();
|
||||
}
|
||||
|
||||
void genericBuzzer::loop() {
|
||||
if (!rtttl::done()) {
|
||||
rtttl::play();
|
||||
|
||||
@@ -30,6 +30,7 @@ class genericBuzzer
|
||||
bool isQuiet(); // get buzzer state on/off
|
||||
void setVolume(uint8_t level); // 0=min..4=max
|
||||
uint8_t getVolume() const { return _volume_level; }
|
||||
void stop(); // stop any playing melody
|
||||
|
||||
private:
|
||||
uint8_t _volume_level = 4;
|
||||
|
||||
Reference in New Issue
Block a user