diff --git a/docs/faq.md b/docs/faq.md index c7d7a118..ec9772e8 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) @@ -661,10 +662,20 @@ Both the Windows and Mac versions of the client app are fully unlocked and are f ### 5.16. Q: Are there any resources that compare MeshCore to other LoRa systems? **A:** Here is a list of MeshCore comparison resources: +- MeshCore vs Meshtastic by austinmesh.org: + - The Comms Channel on YouTube: - MeshCore Advantages by MCarper: - MeshCore vs Meshtastic by austinmesh.org: +### 5.17. Q: What do the buzzer tones on my companion radio mean? + +Other events (incoming direct message, channel message, ack, advert sent) keep their own short fixed signatures. +**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 0eee45ae..75b234e4 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 @@ public: 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 50dc91d2..412f71de 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -457,7 +457,7 @@ public: 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 { @@ -632,6 +632,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: @@ -648,6 +651,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; @@ -937,11 +946,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); @@ -957,8 +966,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 a77ad6e7..0959043d 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -95,6 +95,7 @@ public: 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 09fc8e77..3294d456 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) { @@ -392,7 +401,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!"); @@ -410,9 +419,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"); } @@ -431,11 +443,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 60cd0d04..3fb9fccb 100644 --- a/examples/companion_radio/ui-orig/UITask.h +++ b/examples/companion_radio/ui-orig/UITask.h @@ -67,6 +67,7 @@ public: 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 5b9ebfd3..6b1b8390 100644 --- a/examples/companion_radio/ui-tiny/UITask.cpp +++ b/examples/companion_radio/ui-tiny/UITask.cpp @@ -399,7 +399,7 @@ public: 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 { @@ -486,6 +486,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: @@ -502,6 +505,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; @@ -809,11 +818,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); @@ -829,8 +838,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 344e48b9..eb1e80bb 100644 --- a/examples/companion_radio/ui-tiny/UITask.h +++ b/examples/companion_radio/ui-tiny/UITask.h @@ -103,6 +103,7 @@ public: 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 dde59f5d..a6c003f0 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 53edda85..a48a727e 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