diff --git a/examples/companion_radio/AbstractUITask.h b/examples/companion_radio/AbstractUITask.h index 105f00a1..7cd8fe24 100644 --- a/examples/companion_radio/AbstractUITask.h +++ b/examples/companion_radio/AbstractUITask.h @@ -18,7 +18,7 @@ enum class UIEventType { contactMessage, channelMessage, roomMessage, - newContactMessage, + advertReceived, ack }; diff --git a/examples/companion_radio/DataStore.cpp b/examples/companion_radio/DataStore.cpp index f1d32150..fe21afa9 100644 --- a/examples/companion_radio/DataStore.cpp +++ b/examples/companion_radio/DataStore.cpp @@ -317,6 +317,9 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no rd(&_prefs.trail_units_idx, sizeof(_prefs.trail_units_idx)); rd(&_prefs.ch_fav_bitmask, sizeof(_prefs.ch_fav_bitmask)); rd(&_prefs.ch_fav_only, sizeof(_prefs.ch_fav_only)); + if (file.available() >= (int)(sizeof(_prefs.notif_melody_ad) + sizeof(uint32_t))) { + file.read((uint8_t *)&_prefs.notif_melody_ad, sizeof(_prefs.notif_melody_ad)); + } // Schema sentinel: bumped on layout changes. Mismatch means an older file // (or a different schema); rd() already zero-inits any fields not present, @@ -335,7 +338,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no // 0xC0DE0003 → 0xC0DE0004: trail_units_idx added after trail_min_delta_idx. // On a 0xC0DE0003 file the sentinel bytes sit where trail_units_idx is now, // so rd() picks up 0x03 (low byte of the old sentinel) — reset just that - // case to default 0. Newer mismatches (e.g. 0xC0DE0004 → 0xC0DE0005) had + // case to default 0. Newer mismatches (e.g. 0xC0DE0005 → 0xC0DE0006) had // the field saved correctly and must not be clobbered. if (sentinel == 0xC0DE0003) { _prefs.trail_units_idx = 0; @@ -428,6 +431,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_ file.write((uint8_t *)&_prefs.trail_units_idx, sizeof(_prefs.trail_units_idx)); file.write((uint8_t *)&_prefs.ch_fav_bitmask, sizeof(_prefs.ch_fav_bitmask)); file.write((uint8_t *)&_prefs.ch_fav_only, sizeof(_prefs.ch_fav_only)); + file.write((uint8_t *)&_prefs.notif_melody_ad, sizeof(_prefs.notif_melody_ad)); // Tail sentinel — must be last. See NodePrefs::SCHEMA_SENTINEL. uint32_t sentinel = NodePrefs::SCHEMA_SENTINEL; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index a3c939fb..ad142999 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -360,6 +360,8 @@ void MyMesh::onContactsFull() { } void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) { + if (_ui) _ui->notify(UIEventType::advertReceived); + if (_serial->isConnected()) { if (is_new) { writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact); @@ -368,10 +370,6 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); } - } else { -#ifdef DISPLAY_CLASS - if (_ui) _ui->notify(UIEventType::newContactMessage); -#endif } // add inbound-path to mem cache @@ -920,7 +918,7 @@ void MyMesh::onControlDataRecv(mesh::Packet *packet) { memcpy(r.pub_key, pub_key, PUB_KEY_SIZE); r.timestamp = getRTCClock()->getCurrentTime(); } - if (_ui) _ui->notify(UIEventType::newContactMessage); + if (_ui) _ui->notify(UIEventType::advertReceived); return; // our discover — don't forward to BLE app } } @@ -1090,6 +1088,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe _prefs.ringtone_bpm_idx = 2; // 120 bpm default _prefs.ringtone_len = 0; // no custom ringtone by default _prefs.ringtone2_bpm_idx = 2; // 120 bpm default + _prefs.notif_melody_ad = 0; // built-in advert sound by default _prefs.home_pages_mask = 0x01FF; // all pages visible _prefs.bot_enabled = 0; _prefs.bot_channel_enabled = 0; @@ -2498,7 +2497,7 @@ void MyMesh::loop() { } #ifdef DISPLAY_CLASS - if (_ui) _ui->setHasConnection(_serial->isBLEConnected()); + if (_ui) _ui->setHasConnection(_serial->isConnected()); #endif } diff --git a/examples/companion_radio/NodePrefs.h b/examples/companion_radio/NodePrefs.h index 1538c901..75367a50 100644 --- a/examples/companion_radio/NodePrefs.h +++ b/examples/companion_radio/NodePrefs.h @@ -69,9 +69,10 @@ struct NodePrefs { // persisted to file uint8_t ringtone2_bpm_idx; uint8_t ringtone2_len; uint8_t ringtone2_notes[32]; - // Global melody for notifications: 0=built-in, 1=melody1, 2=melody2 + // Global melodies for notifications: 0=built-in, 1=melody1, 2=melody2 uint8_t notif_melody_dm; uint8_t notif_melody_ch; + uint8_t notif_melody_ad; // Per-channel melody override (2 bitmasks, 1 bit per channel) uint64_t ch_notif_melody_set; // bit i = channel i has explicit melody uint64_t ch_notif_melody_2; // bit i = use melody 2 (else melody 1, when set bit is set) @@ -112,7 +113,7 @@ struct NodePrefs { // persisted to file // adding/removing/reordering fields in DataStore::savePrefs/loadPrefsInt so // older saves are detected on load and skipped (zero-init defaults kept). // High 24 bits identify the file format; low byte is the schema revision. - static const uint32_t SCHEMA_SENTINEL = 0xC0DE0005; + static const uint32_t SCHEMA_SENTINEL = 0xC0DE0006; // Bit-index for each home page. Used by page_order (entries store bit+1) and // by home_pages_mask. Single source of truth — both HomeScreen::pageBit/bitToPage @@ -180,4 +181,4 @@ struct NodePrefs { // persisted to file } if (pos < size) buf[pos] = '\0'; } -}; \ No newline at end of file +}; diff --git a/examples/companion_radio/ui-new/SettingsScreen.h b/examples/companion_radio/ui-new/SettingsScreen.h index dd609e98..a07d3dd4 100644 --- a/examples/companion_radio/ui-new/SettingsScreen.h +++ b/examples/companion_radio/ui-new/SettingsScreen.h @@ -38,6 +38,7 @@ class SettingsScreen : public UIScreen { BUZZER_VOLUME, DM_MELODY, CH_MELODY, + AD_SOUND, // Home pages section SECTION_HOME_PAGES, HOME_CLOCK, HOME_FAVOURITES, HOME_RECENT, HOME_RADIO, HOME_BT, HOME_ADVERT, @@ -445,6 +446,12 @@ class SettingsScreen : public UIScreen { { static const char* L[] = { "built-in", "M1", "M2" }; uint8_t v = p ? p->notif_melody_ch : 0; display.print(L[v < 3 ? v : 0]); } + } else if (item == AD_SOUND) { + display.print("AD sound"); + display.setCursor(display.valCol(), y); + { static const char* L[] = { "built-in", "M1", "M2" }; + uint8_t v = p ? p->notif_melody_ad : 0; + display.print(L[v < 3 ? v : 0]); } } else if (isHomePage(item)) { if (p) ensurePageOrderInit(p); int pos = homePagePosition(item, p); @@ -691,6 +698,10 @@ public: p->notif_melody_ch = (p->notif_melody_ch + (left ? 2 : 1)) % 3; _dirty = true; return true; } + if (_selected == AD_SOUND && p && (left || right || enter)) { + p->notif_melody_ad = (p->notif_melody_ad + (left ? 2 : 1)) % 3; + _dirty = true; return true; + } if (isHomePage(_selected) && p) { if (left || right) { movePageInOrder(_selected, left ? -1 : 1, p); diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 79eb08e4..2c41b777 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -1380,11 +1380,28 @@ switch(t){ _last_notif_ch_idx = -1; break; } + case UIEventType::advertReceived: { + if (!buzzer.isQuiet()) { + int slot = _node_prefs ? (int)_node_prefs->notif_melody_ad : 0; + bool custom_played = false; + if (slot > 0 && _node_prefs) { + if (buzzer.isPlaying()) buzzer.stop(); // stop before overwriting _notif_mel_buf + buildMelodyFromPrefs(_node_prefs, slot, _notif_mel_buf, sizeof(_notif_mel_buf)); + if (_notif_mel_buf[0]) { + buzzer.play(_notif_mel_buf); + custom_played = true; + } + } + if (!custom_played) { + buzzer.play("MsgRcv3:d=4,o=6,b=200:32e,32g,32b,16c7"); + } + } + break; + } case UIEventType::ack: buzzer.play("ack:d=32,o=8,b=120:c"); break; case UIEventType::roomMessage: - case UIEventType::newContactMessage: case UIEventType::none: default: break; diff --git a/examples/companion_radio/ui-orig/UITask.cpp b/examples/companion_radio/ui-orig/UITask.cpp index 3ad36fb0..c96c4aa1 100644 --- a/examples/companion_radio/ui-orig/UITask.cpp +++ b/examples/companion_radio/ui-orig/UITask.cpp @@ -103,7 +103,7 @@ switch(t){ buzzer.play("ack:d=32,o=8,b=120:c"); break; case UIEventType::roomMessage: - case UIEventType::newContactMessage: + case UIEventType::advertReceived: case UIEventType::none: default: break; @@ -443,4 +443,4 @@ void UITask::handleButtonLongPress() { } else { shutdown(); } -} \ No newline at end of file +}