diff --git a/docs/faq.md b/docs/faq.md index c33172462a..f51e8cafe8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -65,6 +65,7 @@ A list of frequently-asked questions and answers for MeshCore - [5.14. Q: Are there projects built around MeshCore?](#514-q-are-there-projects-built-around-meshcore) - [5.15. Q: Are there client applications for Windows or Mac?](#515-q-are-there-client-applications-for-windows-or-mac) - [5.16. Q: Are there any resources that compare MeshCore to other LoRa systems?](#516-q-are-there-any-resources-that-compare-meshcore-to-other-lora-systems) + - [5.17. Q: What do the buzzer tones on my companion radio mean?](#517-q-what-do-the-buzzer-tones-on-my-companion-radio-mean) - [6. Troubleshooting](#6-troubleshooting) - [6.1. Q: My client says another client or a repeater or a room server was last seen many, many days ago.](#61-q-my-client-says-another-client-or-a-repeater-or-a-room-server-was-last-seen-many-many-days-ago) - [6.2. Q: A repeater or a client or a room server I expect to see on my discover list (on T-Deck) or contact list (on a smart device client) are not listed.](#62-q-a-repeater-or-a-client-or-a-room-server-i-expect-to-see-on-my-discover-list-on-t-deck-or-contact-list-on-a-smart-device-client-are-not-listed) @@ -681,6 +682,14 @@ https://github.com/mikecarper/meshfirmware/blob/main/MeshCoreAdvantages.md Meshcore vs Meshtastic by austinmesh.org https://www.austinmesh.org/learn/meshcore-vs-meshtastic/ +### 5.17. Q: What do the buzzer tones on my companion radio mean? + +**A:** On companion-radio devices the buzzer plays distinct tones so you can tell actions apart by ear, which is especially useful on button-only devices like the T1000-E. + +Toggle confirmations follow a simple convention: **ascending pitch = enabled**, **descending pitch = disabled**, and the **number of notes matches the number of button presses** that triggered the action. So a triple-press to toggle the buzzer plays 3 notes (ascending on, descending off), a quadruple-press to toggle GPS plays 4 notes, and so on. + +Other events (incoming direct message, channel message, ack, advert sent) keep their own short fixed signatures. + --- diff --git a/examples/companion_radio/AbstractUITask.h b/examples/companion_radio/AbstractUITask.h index 0eee45aef3..75b234e4d4 100644 --- a/examples/companion_radio/AbstractUITask.h +++ b/examples/companion_radio/AbstractUITask.h @@ -19,7 +19,8 @@ enum class UIEventType { channelMessage, roomMessage, newContactMessage, - ack + ack, + advertSent }; class AbstractUITask { @@ -42,5 +43,6 @@ class AbstractUITask { virtual void msgRead(int msgcount) = 0; virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0; virtual void notify(UIEventType t = UIEventType::none) = 0; + virtual void notifyToggle(int count, bool enabled) {} virtual void loop() = 0; }; diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index ee12ca740d..9c92231505 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -434,7 +434,7 @@ class HomeScreen : public UIScreen { return true; } if (c == KEY_ENTER && _page == HomePage::ADVERT) { - _task->notify(UIEventType::ack); + _task->notify(UIEventType::advertSent); if (the_mesh.advert()) { _task->showAlert("Advert sent!", 1000); } else { @@ -609,6 +609,9 @@ switch(t){ case UIEventType::ack: buzzer.play("ack:d=32,o=8,b=120:c"); break; + case UIEventType::advertSent: + buzzer.play("Advert:d=8,o=6,b=180:c,e,g,c7"); + break; case UIEventType::roomMessage: case UIEventType::newContactMessage: case UIEventType::none: @@ -625,6 +628,12 @@ switch(t){ #endif } +void UITask::notifyToggle(int count, bool enabled) { +#if defined(PIN_BUZZER) + buzzer.playToggle(count, enabled); +#endif +} + void UITask::msgRead(int msgcount) { _msgcount = msgcount; @@ -909,11 +918,11 @@ void UITask::toggleGPS() { if (strcmp(_sensors->getSettingValue(i), "1") == 0) { _sensors->setSettingValue("gps", "0"); _node_prefs->gps_enabled = 0; - notify(UIEventType::ack); + notifyToggle(4, false); } else { _sensors->setSettingValue("gps", "1"); _node_prefs->gps_enabled = 1; - notify(UIEventType::ack); + notifyToggle(4, true); } the_mesh.savePrefs(); showAlert(_node_prefs->gps_enabled ? "GPS: Enabled" : "GPS: Disabled", 800); @@ -929,8 +938,11 @@ void UITask::toggleBuzzer() { #ifdef PIN_BUZZER if (buzzer.isQuiet()) { buzzer.quiet(false); - notify(UIEventType::ack); + notifyToggle(3, true); } else { + // play the off-tone before muting so the user hears the confirmation + notifyToggle(3, false); + while (buzzer.isPlaying()) buzzer.loop(); buzzer.quiet(true); } _node_prefs->buzzer_quiet = buzzer.isQuiet(); diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index a77ad6e7ec..0959043d54 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -95,6 +95,7 @@ class UITask : public AbstractUITask { void msgRead(int msgcount) override; void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override; void notify(UIEventType t = UIEventType::none) override; + void notifyToggle(int count, bool enabled) override; void loop() override; void shutdown(bool restart = false); diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index 5529046775..83aa4c3cc1 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -103,6 +103,9 @@ switch(t){ case UIEventType::ack: buzzer.play("ack:d=32,o=8,b=120:c"); break; + case UIEventType::advertSent: + buzzer.play("Advert:d=8,o=6,b=180:c,e,g,c7"); + break; case UIEventType::roomMessage: case UIEventType::newContactMessage: case UIEventType::none: @@ -114,6 +117,12 @@ switch(t){ // Serial.println((int) t); } +void UITask::notifyToggle(int count, bool enabled) { +#if defined(PIN_BUZZER) + buzzer.playToggle(count, enabled); +#endif +} + void UITask::msgRead(int msgcount) { _msgcount = msgcount; if (msgcount == 0) { @@ -393,7 +402,7 @@ void UITask::handleButtonDoublePress() { MESH_DEBUG_PRINTLN("UITask: double press triggered, sending advert"); // ADVERT #ifdef PIN_BUZZER - notify(UIEventType::ack); + notify(UIEventType::advertSent); #endif if (the_mesh.advert()) { MESH_DEBUG_PRINTLN("Advert sent!"); @@ -411,9 +420,12 @@ void UITask::handleButtonTriplePress() { #ifdef PIN_BUZZER if (buzzer.isQuiet()) { buzzer.quiet(false); - notify(UIEventType::ack); + notifyToggle(3, true); sprintf(_alert, "Buzzer: ON"); } else { + // play the off-tone before muting so the user hears the confirmation + notifyToggle(3, false); + while (buzzer.isPlaying()) buzzer.loop(); buzzer.quiet(true); sprintf(_alert, "Buzzer: OFF"); } @@ -432,11 +444,11 @@ void UITask::handleButtonQuadruplePress() { if (strcmp(_sensors->getSettingName(i), "gps") == 0) { if (strcmp(_sensors->getSettingValue(i), "1") == 0) { _sensors->setSettingValue("gps", "0"); - notify(UIEventType::ack); + notifyToggle(4, false); sprintf(_alert, "GPS: Disabled"); } else { _sensors->setSettingValue("gps", "1"); - notify(UIEventType::ack); + notifyToggle(4, true); sprintf(_alert, "GPS: Enabled"); } break; diff --git a/examples/companion_radio/ui-orig/UITask.h b/examples/companion_radio/ui-orig/UITask.h index 60cd0d042c..3fb9fccbd4 100644 --- a/examples/companion_radio/ui-orig/UITask.h +++ b/examples/companion_radio/ui-orig/UITask.h @@ -67,6 +67,7 @@ class UITask : public AbstractUITask { void msgRead(int msgcount) override; void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override; void notify(UIEventType t = UIEventType::none) override; + void notifyToggle(int count, bool enabled) override; void loop() override; void shutdown(bool restart = false); diff --git a/examples/companion_radio/ui-tiny/UITask.cpp b/examples/companion_radio/ui-tiny/UITask.cpp index 45a07a02ef..03d84e4cff 100644 --- a/examples/companion_radio/ui-tiny/UITask.cpp +++ b/examples/companion_radio/ui-tiny/UITask.cpp @@ -394,7 +394,7 @@ class HomeScreen : public UIScreen { return true; } if (c == KEY_ENTER && _page == HomePage::ADVERT) { - _task->notify(UIEventType::ack); + _task->notify(UIEventType::advertSent); if (the_mesh.advert()) { _task->showAlert("Advert sent!", 1000); } else { @@ -481,6 +481,9 @@ switch(t){ case UIEventType::ack: buzzer.play("ack:d=32,o=8,b=120:c"); break; + case UIEventType::advertSent: + buzzer.play("Advert:d=8,o=6,b=180:c,e,g,c7"); + break; case UIEventType::roomMessage: case UIEventType::newContactMessage: case UIEventType::none: @@ -497,6 +500,12 @@ switch(t){ #endif } +void UITask::notifyToggle(int count, bool enabled) { +#if defined(PIN_BUZZER) + buzzer.playToggle(count, enabled); +#endif +} + void UITask::msgRead(int msgcount) { _msgcount = msgcount; @@ -796,11 +805,11 @@ void UITask::toggleGPS() { if (strcmp(_sensors->getSettingValue(i), "1") == 0) { _sensors->setSettingValue("gps", "0"); _node_prefs->gps_enabled = 0; - notify(UIEventType::ack); + notifyToggle(4, false); } else { _sensors->setSettingValue("gps", "1"); _node_prefs->gps_enabled = 1; - notify(UIEventType::ack); + notifyToggle(4, true); } the_mesh.savePrefs(); showAlert(_node_prefs->gps_enabled ? "GPS: Enabled" : "GPS: Disabled", 800); @@ -816,8 +825,11 @@ void UITask::toggleBuzzer() { #ifdef PIN_BUZZER if (buzzer.isQuiet()) { buzzer.quiet(false); - notify(UIEventType::ack); + notifyToggle(3, true); } else { + // play the off-tone before muting so the user hears the confirmation + notifyToggle(3, false); + while (buzzer.isPlaying()) buzzer.loop(); buzzer.quiet(true); } _node_prefs->buzzer_quiet = buzzer.isQuiet(); diff --git a/examples/companion_radio/ui-tiny/UITask.h b/examples/companion_radio/ui-tiny/UITask.h index 344e48b98f..eb1e80bb32 100644 --- a/examples/companion_radio/ui-tiny/UITask.h +++ b/examples/companion_radio/ui-tiny/UITask.h @@ -103,6 +103,7 @@ class UITask : public AbstractUITask { void msgRead(int msgcount) override; void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override; void notify(UIEventType t = UIEventType::none) override; + void notifyToggle(int count, bool enabled) override; void loop() override; void shutdown(bool restart = false); diff --git a/src/helpers/ui/buzzer.cpp b/src/helpers/ui/buzzer.cpp index dde59f5d48..a6c003f021 100644 --- a/src/helpers/ui/buzzer.cpp +++ b/src/helpers/ui/buzzer.cpp @@ -44,6 +44,21 @@ void genericBuzzer::shutdown() { play(shutdown_song); } +void genericBuzzer::playToggle(int count, bool enabled) { + static const char *notes[] = {"c", "e", "g", "c7", "e7", "g7"}; + const int max_notes = (int)(sizeof(notes) / sizeof(notes[0])); + if (count < 1) count = 1; + if (count > max_notes) count = max_notes; + A static buffer as the library can't play two melodies at once anyway. + static char melody[64]; + int n = snprintf(melody, sizeof(melody), "Tg:d=8,o=6,b=180:"); + for (int i = 0; i < count && n < (int)sizeof(melody); i++) { + int idx = enabled ? i : (count - 1 - i); + n += snprintf(melody + n, sizeof(melody) - n, "%s%s", i ? "," : "", notes[idx]); + } + play(melody); +} + void genericBuzzer::quiet(bool buzzer_state) { _is_quiet = buzzer_state; #ifdef PIN_BUZZER_EN diff --git a/src/helpers/ui/buzzer.h b/src/helpers/ui/buzzer.h index 0a50055282..54343cc449 100644 --- a/src/helpers/ui/buzzer.h +++ b/src/helpers/ui/buzzer.h @@ -21,6 +21,7 @@ class genericBuzzer public: void begin(); // set up buzzer port void play(const char *melody); // Generic play function + void playToggle(int count, bool enabled); // play toggle tone void loop(); // loop driven-nonblocking void startup(); // play startup sound void shutdown(); // play shutdown sound