From 3179ffa4a73269acf50fbc0f953c121cbffa84e6 Mon Sep 17 00:00:00 2001 From: liu weikai Date: Tue, 14 Apr 2026 23:08:04 +0800 Subject: [PATCH] Stabilize GAT562 BLE config flow --- .../gat562_mesh_evb_pro/app_facade_runtime.h | 4 + .../src/app_facade_runtime.cpp | 58 ++++++++++ modules/ui_mono_128x64/src/runtime.cpp | 101 +++++++++++++++--- .../menu/dashboard/dashboard_mesh_widget.cpp | 13 ++- modules/ui_shared/src/ui/ui_status.cpp | 5 +- .../arduino_common/include/ble/ble_manager.h | 5 + .../arduino_common/include/ble/meshcore_ble.h | 5 + .../include/ble/meshtastic_ble.h | 4 + .../arduino_common/src/ble/ble_manager.cpp | 19 +++- .../arduino_common/src/ble/meshcore_ble.cpp | 76 ++++++++++--- .../arduino_common/src/ble/meshtastic_ble.cpp | 100 ++++++++++------- 11 files changed, 312 insertions(+), 78 deletions(-) diff --git a/apps/gat562_mesh_evb_pro/include/apps/gat562_mesh_evb_pro/app_facade_runtime.h b/apps/gat562_mesh_evb_pro/include/apps/gat562_mesh_evb_pro/app_facade_runtime.h index 919775b2..c0be46eb 100644 --- a/apps/gat562_mesh_evb_pro/include/apps/gat562_mesh_evb_pro/app_facade_runtime.h +++ b/apps/gat562_mesh_evb_pro/include/apps/gat562_mesh_evb_pro/app_facade_runtime.h @@ -115,6 +115,9 @@ class AppFacadeRuntime final : public app::IAppBleFacade, void initializeChatRuntime(); void refreshEffectiveIdentity(); void syncSelfPositionFromGps(); + bool consumePostSaveApplySkip(uint8_t bit, const char* label); + void markPostSaveApplySkips(uint8_t mask); + void clearPostSaveApplySkips(); chat::NodeId resolveSelfNodeId() const; const chat::runtime::SelfIdentityProvider* identityProvider() const; @@ -135,6 +138,7 @@ class AppFacadeRuntime final : public app::IAppBleFacade, boards::gat562_mesh_evb_pro::Gat562Board* board_ = nullptr; chat::ui::IChatUiRuntime* chat_ui_runtime_ = nullptr; bool config_save_pending_ = false; + uint8_t post_save_apply_skip_mask_ = 0; uint32_t last_chat_store_flush_ms_ = 0; }; diff --git a/apps/gat562_mesh_evb_pro/src/app_facade_runtime.cpp b/apps/gat562_mesh_evb_pro/src/app_facade_runtime.cpp index bd26fc47..d9153f02 100644 --- a/apps/gat562_mesh_evb_pro/src/app_facade_runtime.cpp +++ b/apps/gat562_mesh_evb_pro/src/app_facade_runtime.cpp @@ -34,6 +34,14 @@ namespace apps::gat562_mesh_evb_pro namespace { constexpr uint32_t kChatStoreFlushIntervalMs = 2000UL; +constexpr uint8_t kSkipApplyMesh = 1U << 0; +constexpr uint8_t kSkipApplyUser = 1U << 1; +constexpr uint8_t kSkipApplyPosition = 1U << 2; +constexpr uint8_t kSkipApplyNetwork = 1U << 3; +constexpr uint8_t kSkipApplyPrivacy = 1U << 4; +constexpr uint8_t kSkipApplyChatDefaults = 1U << 5; +constexpr uint8_t kSkipApplyMaskAll = kSkipApplyMesh | kSkipApplyUser | kSkipApplyPosition | kSkipApplyNetwork | + kSkipApplyPrivacy | kSkipApplyChatDefaults; class ScopedGpsSuspend { @@ -294,6 +302,7 @@ const app::AppConfig& AppFacadeRuntime::getConfig() const void AppFacadeRuntime::saveConfig() { ScopedGpsSuspend suspend_gps(board_); + clearPostSaveApplySkips(); debug_console::printf("[gat562][cfg] save start proto=%u ok_to_mqtt=%u ignore_mqtt=%u ble=%u\n", static_cast(config_.mesh_protocol), config_.meshtastic_config.config_ok_to_mqtt ? 1U : 0U, @@ -316,6 +325,7 @@ void AppFacadeRuntime::saveConfig() applyPrivacyConfig(); debug_console::printf("[gat562][cfg] save post-applyPrivacy\n"); applyChatDefaults(); + markPostSaveApplySkips(kSkipApplyMaskAll); ::boards::gat562_mesh_evb_pro::settings_store::queueSaveAppConfig(config_); config_save_pending_ = true; debug_console::printf("[gat562][cfg] save deferred-store queued\n"); @@ -323,6 +333,10 @@ void AppFacadeRuntime::saveConfig() void AppFacadeRuntime::applyMeshConfig() { + if (consumePostSaveApplySkip(kSkipApplyMesh, "applyMesh")) + { + return; + } if (apply_service_) { apply_service_->applyMesh(config_, @@ -335,6 +349,10 @@ void AppFacadeRuntime::applyMeshConfig() void AppFacadeRuntime::applyUserInfo() { + if (consumePostSaveApplySkip(kSkipApplyUser, "applyUser")) + { + return; + } const chat::runtime::EffectiveSelfIdentity previous_identity = effective_identity_; refreshEffectiveIdentity(); if (apply_service_) @@ -348,6 +366,10 @@ void AppFacadeRuntime::applyUserInfo() void AppFacadeRuntime::applyPositionConfig() { + if (consumePostSaveApplySkip(kSkipApplyPosition, "applyPos")) + { + return; + } if (apply_service_) { apply_service_->applyPosition(config_, board_); @@ -366,6 +388,10 @@ chat::NodeId AppFacadeRuntime::resolveSelfNodeId() const void AppFacadeRuntime::applyNetworkLimits() { + if (consumePostSaveApplySkip(kSkipApplyNetwork, "applyLimits")) + { + return; + } if (mesh_router_) { mesh_router_->setNetworkLimits(config_.net_duty_cycle, config_.net_channel_util); @@ -374,6 +400,10 @@ void AppFacadeRuntime::applyNetworkLimits() void AppFacadeRuntime::applyPrivacyConfig() { + if (consumePostSaveApplySkip(kSkipApplyPrivacy, "applyPrivacy")) + { + return; + } if (mesh_router_) { mesh_router_->setPrivacyConfig(config_.privacy_encrypt_mode, config_.privacy_pki); @@ -382,6 +412,10 @@ void AppFacadeRuntime::applyPrivacyConfig() void AppFacadeRuntime::applyChatDefaults() { + if (consumePostSaveApplySkip(kSkipApplyChatDefaults, "applyChatDefaults")) + { + return; + } if (!chat_service_) { return; @@ -649,6 +683,7 @@ const BoardBase* AppFacadeRuntime::getBoard() const void AppFacadeRuntime::updateCoreServices() { + clearPostSaveApplySkips(); syncSelfPositionFromGps(); if (chat_service_) { @@ -674,6 +709,29 @@ void AppFacadeRuntime::updateCoreServices() } } +bool AppFacadeRuntime::consumePostSaveApplySkip(uint8_t bit, const char* label) +{ + if ((post_save_apply_skip_mask_ & bit) == 0) + { + return false; + } + + post_save_apply_skip_mask_ &= static_cast(~bit); + debug_console::printf("[gat562][cfg] %s skipped: already applied in save\n", + label ? label : "apply"); + return true; +} + +void AppFacadeRuntime::markPostSaveApplySkips(uint8_t mask) +{ + post_save_apply_skip_mask_ |= mask; +} + +void AppFacadeRuntime::clearPostSaveApplySkips() +{ + post_save_apply_skip_mask_ = 0; +} + void AppFacadeRuntime::syncSelfPositionFromGps() { if (!contact_service_ || !board_ || effective_identity_.node_id == 0) diff --git a/modules/ui_mono_128x64/src/runtime.cpp b/modules/ui_mono_128x64/src/runtime.cpp index ec7d6e52..3c2eaea0 100644 --- a/modules/ui_mono_128x64/src/runtime.cpp +++ b/modules/ui_mono_128x64/src/runtime.cpp @@ -1108,6 +1108,58 @@ bool loadBlePairingStatus(app::IAppFacade* app, ble::BlePairingStatus* out) return manager ? manager->getPairingStatus(out) : false; } +enum class BleDisplayState +{ + Off, + On, + Link, +}; + +BleDisplayState resolveBleDisplayState(app::IAppFacade* app) +{ + if (!app) + { + return BleDisplayState::Off; + } + + ble::BleManager* manager = app->getBleManager(); + if (!manager || !manager->isEnabled()) + { + return BleDisplayState::Off; + } + + ble::BlePairingStatus status{}; + if (manager->getPairingStatus(&status) && status.is_connected) + { + return BleDisplayState::Link; + } + + return BleDisplayState::On; +} + +const char* bleDisplayStateLabel(BleDisplayState state) +{ + switch (state) + { + case BleDisplayState::Link: + return "LINK"; + case BleDisplayState::On: + return "ON"; + case BleDisplayState::Off: + default: + return "OFF"; + } +} + +void formatBleStateLabel(char* out, size_t out_len, app::IAppFacade* app) +{ + if (!out || out_len == 0) + { + return; + } + std::snprintf(out, out_len, "BLE %s", bleDisplayStateLabel(resolveBleDisplayState(app))); +} + const char* blePairingModeLabel(const ble::BlePairingStatus& status) { if (!status.requires_passkey) @@ -1882,6 +1934,7 @@ void Runtime::render() ble::BlePairingStatus ble_status{}; if (loadBlePairingStatus(app(), &ble_status) && + ble_status.available && ble_status.requires_passkey && ble_status.is_pairing_active) { @@ -1937,6 +1990,7 @@ void Runtime::renderScreensaver() { char protocol[8] = {}; char freq[20] = {}; + char ram_buf[24] = {}; char time_buf[16] = {}; char time_main_buf[8] = {}; char time_sec_buf[4] = {}; @@ -1950,7 +2004,7 @@ void Runtime::renderScreensaver() char top_left_buf[32] = {}; char top_right_buf[32] = {}; char left_toggle_buf[12] = {}; - char right_toggle_buf[12] = {}; + char left_ble_buf[12] = {}; formatProtocol(protocol, sizeof(protocol)); formatNodeLabel(node_buf, sizeof(node_buf)); formatTime(time_buf, sizeof(time_buf), date_buf, sizeof(date_buf)); @@ -1965,11 +2019,11 @@ void Runtime::renderScreensaver() refreshGnssSnapshot(); const auto battery = host_.battery_info_fn ? host_.battery_info_fn() : platform::ui::device::BatteryInfo{}; + const auto ram = host_.ram_usage_fn ? host_.ram_usage_fn() : HostCallbacks::ResourceUsage{}; const auto& gps = gnss_snapshot_state_; const auto& gnss_status = gnss_snapshot_status_; const int unread = app() ? app()->getChatService().getTotalUnread() : 0; const bool gps_enabled = host_.gps_enabled_fn && host_.gps_enabled_fn(); - const bool ble_enabled = app() && app()->isBleEnabled(); std::snprintf(unread_buf, sizeof(unread_buf), unread > 99 ? "99+" : "%d", unread); if (battery.available && battery.level >= 0) { @@ -2008,14 +2062,26 @@ void Runtime::renderScreensaver() std::snprintf(top_left_buf, sizeof(top_left_buf), "%s %s", protocol[0] ? protocol : "--", bat_pct_buf); std::snprintf(top_right_buf, sizeof(top_right_buf), "%s", freq[0] ? freq : "--"); formatToggleLabel(left_toggle_buf, sizeof(left_toggle_buf), "GPS", gps_enabled); - formatToggleLabel(right_toggle_buf, sizeof(right_toggle_buf), "BLE", ble_enabled); + formatBleStateLabel(left_ble_buf, sizeof(left_ble_buf), app()); + if (ram.available && ram.total_bytes > 0) + { + std::snprintf(ram_buf, sizeof(ram_buf), "RAM %lu/%luK", + static_cast(ram.used_bytes / 1024U), + static_cast(ram.total_bytes / 1024U)); + } + else + { + std::snprintf(ram_buf, sizeof(ram_buf), "RAM --"); + } constexpr int kTopY = 1; constexpr int kTopDetailY = 9; - constexpr int kTimeY = 22; - constexpr int kSideToggleY = 26; - constexpr int kSecY = 28; - constexpr int kDateY = 42; + constexpr int kRamY = 17; + constexpr int kTimeY = 24; + constexpr int kSidePrimaryY = 27; + constexpr int kSideSecondaryY = 35; + constexpr int kSecY = 30; + constexpr int kDateY = 44; constexpr int kFooterY = 55; drawTextClipped(2, kTopY, 60, top_left_buf); @@ -2026,6 +2092,8 @@ void Runtime::renderScreensaver() std::snprintf(top_detail_right, sizeof(top_detail_right), "MSG %s", unread_buf); const int top_detail_right_w = text_renderer_.measureTextWidth(top_detail_right); text_renderer_.drawText(display_, std::max(70, display_.width() - top_detail_right_w - 2), kTopDetailY, top_detail_right); + const int ram_w = text_renderer_.measureTextWidth(ram_buf); + text_renderer_.drawText(display_, std::max(64, display_.width() - ram_w - 2), kRamY, ram_buf); const int time_w = measureClockText(time_main_buf); const int time_x = std::max(0, (display_.width() - time_w) / 2); @@ -2035,9 +2103,8 @@ void Runtime::renderScreensaver() const int sec_x = std::min(display_.width() - 12, time_x + time_w + 4); text_renderer_.drawText(display_, sec_x, kSecY, time_sec_buf); } - drawTextClipped(0, kSideToggleY, 28, left_toggle_buf); - const int right_toggle_w = text_renderer_.measureTextWidth(right_toggle_buf); - text_renderer_.drawText(display_, std::max(96, display_.width() - right_toggle_w), kSideToggleY, right_toggle_buf); + drawTextClipped(0, kSidePrimaryY, 42, left_toggle_buf); + drawTextClipped(0, kSideSecondaryY, 42, left_ble_buf); const int status_w = text_renderer_.measureTextWidth(status_buf); text_renderer_.drawText(display_, std::max(0, (display_.width() - status_w) / 2), kDateY, status_buf); @@ -2788,20 +2855,20 @@ void Runtime::renderDeviceSettings() { if (i == 0) { - if (has_ble_status && ble_status.requires_passkey && ble_status.passkey != 0) + if (has_ble_status && ble_status.available && ble_status.requires_passkey && ble_status.passkey != 0) { std::snprintf(line, sizeof(line), "BLE: ON %s %06lu", ble_status.is_fixed_pin ? "FIX" : "PIN", static_cast(ble_status.passkey)); } - else if (has_ble_status && ble_status.requires_passkey) + else if (has_ble_status && ble_status.available && ble_status.requires_passkey) { std::snprintf(line, sizeof(line), "BLE: ON %s", ble_status.is_fixed_pin ? "FIXED" : "RANDOM"); } else { - std::snprintf(line, sizeof(line), "BLE: %s", app()->isBleEnabled() ? "ON" : "OFF"); + std::snprintf(line, sizeof(line), "BLE: %s", bleDisplayStateLabel(resolveBleDisplayState(app()))); } } else if (i == 1) @@ -2961,8 +3028,8 @@ void Runtime::renderInfoPage() char value[40] = {}; std::snprintf(value, sizeof(value), "%s", protocolLabel(cfg.mesh_protocol)); push_kv("PROTO", value); - push_kv("BLE", app()->isBleEnabled() ? "ON" : "OFF"); - if (has_ble_status && ble_status.requires_passkey) + push_kv("BLE", bleDisplayStateLabel(resolveBleDisplayState(app()))); + if (has_ble_status && ble_status.available && ble_status.requires_passkey) { push_kv("BLE MODE", blePairingModeLabel(ble_status)); if (ble_status.passkey != 0) @@ -3006,8 +3073,8 @@ void Runtime::renderInfoPage() } push_line("[SYSTEM]"); - push_kv("BLE", app()->isBleEnabled() ? "ON" : "OFF"); - if (has_ble_status && ble_status.requires_passkey) + push_kv("BLE", bleDisplayStateLabel(resolveBleDisplayState(app()))); + if (has_ble_status && ble_status.available && ble_status.requires_passkey) { push_kv("BLE MODE", blePairingModeLabel(ble_status)); if (ble_status.passkey != 0) diff --git a/modules/ui_shared/src/ui/menu/dashboard/dashboard_mesh_widget.cpp b/modules/ui_shared/src/ui/menu/dashboard/dashboard_mesh_widget.cpp index 5bc47669..2076e6ab 100644 --- a/modules/ui_shared/src/ui/menu/dashboard/dashboard_mesh_widget.cpp +++ b/modules/ui_shared/src/ui/menu/dashboard/dashboard_mesh_widget.cpp @@ -166,10 +166,21 @@ void refresh_mesh_widget() lv_obj_set_style_text_color(mesh.stat_values[2], battery.charging ? color_info() : color_text(), 0); char footer[64]; + bool ble_active = false; + bool ble_linked = false; + if (auto* ble = app::runtimeFacade().getBleManager()) + { + ble_active = ble->isEnabled(); + ble::BlePairingStatus ble_status{}; + if (ble->getPairingStatus(&ble_status)) + { + ble_linked = ble_status.is_connected; + } + } std::snprintf(footer, sizeof(footer), "%s | %s", - app::runtimeFacade().isBleEnabled() ? "BLE bridge ready" : "LoRa direct path", + ble_linked ? "BLE linked" : (ble_active ? "BLE bridge ready" : "LoRa direct path"), unread > 0 ? "new activity" : "quiet net"); set_label_text_if_changed(mesh.footer_label, footer); diff --git a/modules/ui_shared/src/ui/ui_status.cpp b/modules/ui_shared/src/ui/ui_status.cpp index c650cd94..cc7c878f 100644 --- a/modules/ui_shared/src/ui/ui_status.cpp +++ b/modules/ui_shared/src/ui/ui_status.cpp @@ -113,7 +113,10 @@ StatusSnapshot collect_status() snap.route_active = cfg.route_enabled && (cfg.route_path[0] != '\0'); snap.track_recording = platform::ui::tracker::is_recording(); snap.gps_enabled = platform::ui::gps::is_enabled(); - snap.ble_enabled = app::runtimeFacade().isBleEnabled(); + if (auto* ble = app::runtimeFacade().getBleManager()) + { + snap.ble_enabled = ble->isEnabled(); + } refresh_team_cache(); snap.team_active = s_team_cache.team_active; diff --git a/platform/nrf52/arduino_common/include/ble/ble_manager.h b/platform/nrf52/arduino_common/include/ble/ble_manager.h index a855ffb9..bd6242de 100644 --- a/platform/nrf52/arduino_common/include/ble/ble_manager.h +++ b/platform/nrf52/arduino_common/include/ble/ble_manager.h @@ -43,6 +43,11 @@ class BleService virtual void start() = 0; virtual void stop() = 0; virtual void update() = 0; + virtual bool isRunning() const { return false; } + virtual void setDeviceName(const std::string& name) + { + (void)name; + } virtual bool getPairingStatus(BlePairingStatus* out) const { (void)out; diff --git a/platform/nrf52/arduino_common/include/ble/meshcore_ble.h b/platform/nrf52/arduino_common/include/ble/meshcore_ble.h index eecfc0cd..a02b75ab 100644 --- a/platform/nrf52/arduino_common/include/ble/meshcore_ble.h +++ b/platform/nrf52/arduino_common/include/ble/meshcore_ble.h @@ -25,6 +25,9 @@ class MeshCoreBleService final : public BleService, void start() override; void stop() override; void update() override; + bool isRunning() const override; + void setDeviceName(const std::string& name) override; + bool getPairingStatus(BlePairingStatus* out) const override; void onIncomingText(const chat::MeshIncomingText& msg) override; bool handleRxFrame(const uint8_t* data, size_t len); @@ -41,6 +44,8 @@ class MeshCoreBleService final : public BleService, ::BLECharacteristic rx_char_; ::BLECharacteristic tx_char_; bool active_ = false; + bool gatt_initialized_ = false; + bool observer_registered_ = false; std::unique_ptr core_; }; diff --git a/platform/nrf52/arduino_common/include/ble/meshtastic_ble.h b/platform/nrf52/arduino_common/include/ble/meshtastic_ble.h index ece49f5e..003a62f7 100644 --- a/platform/nrf52/arduino_common/include/ble/meshtastic_ble.h +++ b/platform/nrf52/arduino_common/include/ble/meshtastic_ble.h @@ -46,6 +46,8 @@ class MeshtasticBleService final : public BleService, void start() override; void stop() override; void update() override; + bool isRunning() const override; + void setDeviceName(const std::string& name) override; void onIncomingText(const chat::MeshIncomingText& msg) override; void onOutgoingText(const chat::MeshIncomingText& msg) override; @@ -109,6 +111,8 @@ class MeshtasticBleService final : public BleService, ::BLECharacteristic from_num_; ::BLECharacteristic log_radio_; bool active_ = false; + bool gatt_initialized_ = false; + bool observers_registered_ = false; bool connected_ = false; bool from_num_notify_enabled_ = false; uint16_t conn_handle_ = BLE_CONN_HANDLE_INVALID; diff --git a/platform/nrf52/arduino_common/src/ble/ble_manager.cpp b/platform/nrf52/arduino_common/src/ble/ble_manager.cpp index bf7e8740..a292f758 100644 --- a/platform/nrf52/arduino_common/src/ble/ble_manager.cpp +++ b/platform/nrf52/arduino_common/src/ble/ble_manager.cpp @@ -56,14 +56,24 @@ void BleManager::begin() bleManagerLog("[BLE][nrf52] begin enabled=%u proto=%u", ctx_.bleEnabled() ? 1U : 0U, static_cast(ctx_.bleConfig().mesh_protocol)); - setEnabled(true); + setEnabled(ctx_.bleEnabled()); } void BleManager::setEnabled(bool enabled) { if (enabled) { - if (!service_) + if (service_) + { + service_->setDeviceName(buildDeviceName(active_protocol_)); + if (!service_->isRunning()) + { + bleManagerLog("[BLE][nrf52] setEnabled resume proto=%u", + static_cast(active_protocol_)); + service_->start(); + } + } + else { bleManagerLog("[BLE][nrf52] setEnabled on proto=%u", static_cast(ctx_.bleConfig().mesh_protocol)); @@ -72,18 +82,17 @@ void BleManager::setEnabled(bool enabled) } else { - if (service_) + if (service_ && service_->isRunning()) { bleManagerLog("[BLE][nrf52] setEnabled off"); service_->stop(); - service_.reset(); } } } bool BleManager::isEnabled() const { - return ctx_.bleEnabled(); + return service_ && service_->isRunning(); } void BleManager::update() diff --git a/platform/nrf52/arduino_common/src/ble/meshcore_ble.cpp b/platform/nrf52/arduino_common/src/ble/meshcore_ble.cpp index 6c4284fc..b20adace 100644 --- a/platform/nrf52/arduino_common/src/ble/meshcore_ble.cpp +++ b/platform/nrf52/arduino_common/src/ble/meshcore_ble.cpp @@ -15,6 +15,16 @@ namespace constexpr size_t kMaxFrameSize = 172; MeshCoreBleService* s_active_service = nullptr; +void disconnectAll() +{ + uint16_t handles[BLE_MAX_CONNECTION] = {}; + const uint8_t count = Bluefruit.getConnectedHandles(handles, BLE_MAX_CONNECTION); + for (uint8_t i = 0; i < count; ++i) + { + Bluefruit.disconnect(handles[i]); + } +} + void copyBounded(char* dst, size_t dst_len, const char* src) { if (!dst || dst_len == 0) @@ -150,30 +160,43 @@ void MeshCoreBleService::start() s_active_service = this; prepareBluefruit(device_name_); - service_.begin(); + if (!gatt_initialized_) + { + service_.begin(); - rx_char_.setProperties(CHR_PROPS_WRITE); - rx_char_.setPermission(SECMODE_OPEN, SECMODE_OPEN); - rx_char_.setFixedLen(0); - rx_char_.setMaxLen(kMaxFrameSize); - rx_char_.setWriteCallback(onRxWrite, false); - rx_char_.begin(); + rx_char_.setProperties(CHR_PROPS_WRITE); + rx_char_.setPermission(SECMODE_OPEN, SECMODE_OPEN); + rx_char_.setFixedLen(0); + rx_char_.setMaxLen(kMaxFrameSize); + rx_char_.setWriteCallback(onRxWrite, false); + rx_char_.begin(); - tx_char_.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ); - tx_char_.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - tx_char_.setFixedLen(0); - tx_char_.setMaxLen(kMaxFrameSize); - tx_char_.setReadAuthorizeCallback(onTxAuthorize, false); - tx_char_.begin(); + tx_char_.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ); + tx_char_.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + tx_char_.setFixedLen(0); + tx_char_.setMaxLen(kMaxFrameSize); + tx_char_.setReadAuthorizeCallback(onTxAuthorize, false); + tx_char_.begin(); + gatt_initialized_ = true; + } - ctx_.getChatService().addIncomingTextObserver(this); + if (!observer_registered_) + { + ctx_.getChatService().addIncomingTextObserver(this); + observer_registered_ = true; + } startAdvertising(service_); active_ = true; } void MeshCoreBleService::stop() { - ctx_.getChatService().removeIncomingTextObserver(this); + if (observer_registered_) + { + ctx_.getChatService().removeIncomingTextObserver(this); + observer_registered_ = false; + } + disconnectAll(); Bluefruit.Advertising.stop(); if (core_) { @@ -241,6 +264,29 @@ void MeshCoreBleService::sendPendingNotifications() } } +bool MeshCoreBleService::isRunning() const +{ + return active_ && (Bluefruit.connected() || Bluefruit.Advertising.isRunning()); +} + +void MeshCoreBleService::setDeviceName(const std::string& name) +{ + device_name_ = name; +} + +bool MeshCoreBleService::getPairingStatus(BlePairingStatus* out) const +{ + if (!out) + { + return false; + } + + *out = BlePairingStatus{}; + out->available = active_; + out->is_connected = Bluefruit.connected(); + return true; +} + bool MeshCoreBleService::getCustomVars(std::string* out) const { if (!out) diff --git a/platform/nrf52/arduino_common/src/ble/meshtastic_ble.cpp b/platform/nrf52/arduino_common/src/ble/meshtastic_ble.cpp index 462ef329..0dedeaff 100644 --- a/platform/nrf52/arduino_common/src/ble/meshtastic_ble.cpp +++ b/platform/nrf52/arduino_common/src/ble/meshtastic_ble.cpp @@ -549,44 +549,52 @@ void MeshtasticBleService::start() prepareBluefruit(device_name_); applyBleSecurity(); - service_.begin(); - bleLogBoth("[BLE][nrf52][mt] service begin"); - - to_radio_.setProperties(CHR_PROPS_WRITE); - to_radio_.setPermission(SECMODE_OPEN, SECMODE_OPEN); - to_radio_.setFixedLen(0); - to_radio_.setMaxLen(meshtastic_ToRadio_size); - to_radio_.setWriteCallback(onToRadioWrite, false); - to_radio_.begin(); - - from_radio_.setProperties(CHR_PROPS_READ); - from_radio_.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - from_radio_.setFixedLen(0); - from_radio_.setMaxLen(meshtastic_FromRadio_size); - from_radio_.setReadAuthorizeCallback(onFromRadioAuthorize, false); - from_radio_.begin(); + if (!gatt_initialized_) { - uint8_t empty = 0; - from_radio_.write(&empty, 0); + service_.begin(); + bleLogBoth("[BLE][nrf52][mt] service begin"); + + to_radio_.setProperties(CHR_PROPS_WRITE); + to_radio_.setPermission(SECMODE_OPEN, SECMODE_OPEN); + to_radio_.setFixedLen(0); + to_radio_.setMaxLen(meshtastic_ToRadio_size); + to_radio_.setWriteCallback(onToRadioWrite, false); + to_radio_.begin(); + + from_radio_.setProperties(CHR_PROPS_READ); + from_radio_.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + from_radio_.setFixedLen(0); + from_radio_.setMaxLen(meshtastic_FromRadio_size); + from_radio_.setReadAuthorizeCallback(onFromRadioAuthorize, false); + from_radio_.begin(); + { + uint8_t empty = 0; + from_radio_.write(&empty, 0); + } + + from_num_.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ); + from_num_.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + from_num_.setFixedLen(4); + from_num_.write32(0); + from_num_.setCccdWriteCallback(onFromNumCccdWrite, false); + from_num_.begin(); + + log_radio_.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ); + log_radio_.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); + log_radio_.setFixedLen(0); + log_radio_.setMaxLen(96); + log_radio_.begin(); + bleLogBoth("[BLE][nrf52][mt] chars ready"); + gatt_initialized_ = true; } - from_num_.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ); - from_num_.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - from_num_.setFixedLen(4); - from_num_.write32(0); - from_num_.setCccdWriteCallback(onFromNumCccdWrite, false); - from_num_.begin(); - - log_radio_.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ); - log_radio_.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); - log_radio_.setFixedLen(0); - log_radio_.setMaxLen(96); - log_radio_.begin(); - bleLogBoth("[BLE][nrf52][mt] chars ready"); - - ctx_.getChatService().addIncomingTextObserver(this); - ctx_.getChatService().addOutgoingTextObserver(this); - ctx_.getChatService().addIncomingDataObserver(this); + if (!observers_registered_) + { + ctx_.getChatService().addIncomingTextObserver(this); + ctx_.getChatService().addOutgoingTextObserver(this); + ctx_.getChatService().addIncomingDataObserver(this); + observers_registered_ = true; + } startAdvertising(service_); active_ = true; @@ -597,9 +605,13 @@ void MeshtasticBleService::start() void MeshtasticBleService::stop() { - ctx_.getChatService().removeIncomingTextObserver(this); - ctx_.getChatService().removeOutgoingTextObserver(this); - ctx_.getChatService().removeIncomingDataObserver(this); + if (observers_registered_) + { + ctx_.getChatService().removeIncomingTextObserver(this); + ctx_.getChatService().removeOutgoingTextObserver(this); + ctx_.getChatService().removeIncomingDataObserver(this); + observers_registered_ = false; + } disconnectAll(); Bluefruit.Advertising.stop(); @@ -708,6 +720,16 @@ void MeshtasticBleService::onIncomingData(const chat::MeshIncomingData& msg) } } +bool MeshtasticBleService::isRunning() const +{ + return active_ && (Bluefruit.connected() || Bluefruit.Advertising.isRunning()); +} + +void MeshtasticBleService::setDeviceName(const std::string& name) +{ + device_name_ = name; +} + bool MeshtasticBleService::handleToRadio(const uint8_t* data, size_t len) { last_ble_activity_ms_ = millis(); @@ -1096,7 +1118,7 @@ bool MeshtasticBleService::getPairingStatus(BlePairingStatus* out) const } *out = BlePairingStatus{}; - out->available = ctx_.isBleEnabled(); + out->available = active_; out->requires_passkey = ble_config_.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN; out->is_fixed_pin = ble_config_.mode == meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN; out->is_connected = isBleConnected();