Stabilize nRF BLE reads and share pager tone

This commit is contained in:
liu weikai committed 2026-07-01 01:01:52 +08:00
1 parent 8540fe256b
commit 556a50dac7
8 files changed
+447 -118

No files matched your search

+29 -12
View File
@@ -3,8 +3,7 @@
#include "board/sd_utils.h"
#include "display/drivers/ST7789TDeck.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include <AudioFileSourcePROGMEM.h>
#include <AudioGeneratorRTTTL.h>
#include "platform/ui/audio/pager_notification_tone.h"
#include <AudioOutputI2S.h>
#include <Wire.h>
#include <ctime>
@@ -1349,7 +1348,8 @@ void TDeckBoard::playMessageTone()
return;
}
static const char kMessageToneRtttl[] = "MsgRcv:d=4,o=6,b=200:32e,32g,32b,16c7";
static constexpr size_t kFramesPerChunk = 128;
namespace pager_tone = ::platform::ui::audio::pager_notification;
AudioOutputI2S audio_out(1, AudioOutputI2S::EXTERNAL_I2S);
#if defined(DAC_I2S_MCLK)
@@ -1357,6 +1357,9 @@ void TDeckBoard::playMessageTone()
#else
audio_out.SetPinout(DAC_I2S_BCK, DAC_I2S_WS, DAC_I2S_DOUT);
#endif
audio_out.SetRate(pager_tone::kPlaybackSampleRateHz);
audio_out.SetBitsPerSample(16);
audio_out.SetChannels(pager_tone::kChannels);
float gain = static_cast<float>(message_tone_volume_) / 250.0f;
if (gain < 0.0f)
{
@@ -1368,21 +1371,35 @@ void TDeckBoard::playMessageTone()
}
audio_out.SetGain(gain);
AudioFileSourcePROGMEM song(kMessageToneRtttl, sizeof(kMessageToneRtttl) - 1);
AudioGeneratorRTTTL generator;
if (generator.begin(&song, &audio_out))
if (audio_out.begin())
{
const uint32_t deadline = millis() + 1600;
while (generator.isRunning() && millis() < deadline)
const uint32_t deadline = millis() + 1600U;
int16_t pcm[kFramesPerChunk * pager_tone::kChannels];
pager_tone::AdpcmPlaybackState tone_state{};
while (pager_tone::hasMore(tone_state) && millis() < deadline)
{
if (!generator.loop())
const uint16_t frames = pager_tone::fillStereoInterleaved(
tone_state, pcm, static_cast<uint16_t>(kFramesPerChunk));
if (frames == 0U)
{
break;
}
delay(1);
uint16_t written = 0U;
while (written < frames && millis() < deadline)
{
const uint16_t consumed =
audio_out.ConsumeSamples(&pcm[written * pager_tone::kChannels],
static_cast<uint16_t>(frames - written));
if (consumed == 0U)
{
delay(1);
continue;
}
written = static_cast<uint16_t>(written + consumed);
}
}
generator.stop();
audio_out.flush();
}
audio_out.stop();
s_playing = false;
+30 -11
View File
@@ -3,8 +3,6 @@
#include "boards/tdeck_pro/tdeck_pro_board.h"
#include <Arduino.h>
#include <AudioFileSourcePROGMEM.h>
#include <AudioGeneratorRTTTL.h>
#include <AudioOutputI2S.h>
#include <SPI.h>
#include <Wire.h>
@@ -16,6 +14,7 @@
#include <sys/time.h>
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include "platform/ui/audio/pager_notification_tone.h"
namespace boards::tdeck_pro
{
@@ -505,11 +504,16 @@ void TDeckProBoard::playMessageTone()
return;
}
static const char kMessageToneRtttl[] = "Msg:d=4,o=6,b=200:32e,32g,32b,16c7";
static constexpr size_t kFramesPerChunk = 128;
namespace pager_tone = ::platform::ui::audio::pager_notification;
AudioOutputI2S audio_out(1, AudioOutputI2S::EXTERNAL_I2S);
audio_out.SetPinout(profile().optional.i2s_bclk,
profile().optional.i2s_lrc,
profile().optional.i2s_dout);
audio_out.SetRate(pager_tone::kPlaybackSampleRateHz);
audio_out.SetBitsPerSample(16);
audio_out.SetChannels(pager_tone::kChannels);
float gain = static_cast<float>(message_tone_volume_) / 250.0f;
if (gain > 0.40f)
{
@@ -517,20 +521,35 @@ void TDeckProBoard::playMessageTone()
}
audio_out.SetGain(gain);
AudioFileSourcePROGMEM song(kMessageToneRtttl, sizeof(kMessageToneRtttl) - 1);
AudioGeneratorRTTTL generator;
if (generator.begin(&song, &audio_out))
if (audio_out.begin())
{
const uint32_t deadline = millis() + 1600;
while (generator.isRunning() && millis() < deadline)
const uint32_t deadline = millis() + 1600U;
int16_t pcm[kFramesPerChunk * pager_tone::kChannels];
pager_tone::AdpcmPlaybackState tone_state{};
while (pager_tone::hasMore(tone_state) && millis() < deadline)
{
if (!generator.loop())
const uint16_t frames = pager_tone::fillStereoInterleaved(
tone_state, pcm, static_cast<uint16_t>(kFramesPerChunk));
if (frames == 0U)
{
break;
}
delay(1);
uint16_t written = 0U;
while (written < frames && millis() < deadline)
{
const uint16_t consumed =
audio_out.ConsumeSamples(&pcm[written * pager_tone::kChannels],
static_cast<uint16_t>(frames - written));
if (consumed == 0U)
{
delay(1);
continue;
}
written = static_cast<uint16_t>(written + consumed);
}
}
generator.stop();
audio_out.flush();
}
audio_out.stop();
}
+14 -57
View File
@@ -9,7 +9,6 @@
#include "freertos/timers.h"
#include <cmath>
#include <cstring>
#include <driver/gpio.h>
#include <esp_heap_caps.h>
#include <esp_sleep.h>
@@ -19,6 +18,7 @@
#include "display/drivers/ST7796.h"
#include "pins_arduino.h"
#include "platform/esp/arduino_common/storage/sd_card_runtime.h"
#include "platform/ui/audio/pager_notification_tone.h"
#include "platform/ui/settings_store.h"
#include "ui/runtime/ui_feedback.h"
#include <Preferences.h>
@@ -1093,23 +1093,8 @@ void TLoRaPagerBoard::playMessageTone()
s_playing = true;
s_last_play_ms = now;
struct ToneStep
{
uint16_t freq_hz;
uint16_t duration_ms;
};
static constexpr ToneStep kTone[] = {
{1319, 55},
{1568, 55},
{1976, 90},
};
static constexpr uint16_t kGapMs = 18;
static constexpr uint32_t kSampleRate = 16000;
static constexpr uint8_t kChannels = 2;
static constexpr size_t kFramesPerChunk = 128;
static constexpr float kAmplitude = 0.16f;
static constexpr float kTwoPi = 6.28318530718f;
namespace pager_tone = ::platform::ui::audio::pager_notification;
int prev_volume = codec.getVolume();
if (prev_volume < 0 || prev_volume > 100)
@@ -1118,7 +1103,8 @@ void TLoRaPagerBoard::playMessageTone()
}
bool prev_out_mute = codec.getOutMute();
bool opened = (codec.open(16, kChannels, kSampleRate) == 0);
bool opened =
(codec.open(16, pager_tone::kChannels, pager_tone::kPlaybackSampleRateHz) == 0);
if (!opened)
{
s_playing = false;
@@ -1128,48 +1114,19 @@ void TLoRaPagerBoard::playMessageTone()
codec.setOutMute(false);
codec.setVolume(_message_tone_volume);
int16_t pcm[kFramesPerChunk * kChannels];
auto write_silence = [&](uint32_t ms)
int16_t pcm[kFramesPerChunk * pager_tone::kChannels];
pager_tone::AdpcmPlaybackState tone_state{};
while (pager_tone::hasMore(tone_state))
{
uint32_t remaining = (kSampleRate * ms) / 1000;
while (remaining > 0)
const uint16_t frames = pager_tone::fillStereoInterleaved(
tone_state, pcm, static_cast<uint16_t>(kFramesPerChunk));
if (frames == 0)
{
size_t frames = remaining > kFramesPerChunk ? kFramesPerChunk : static_cast<size_t>(remaining);
memset(pcm, 0, frames * kChannels * sizeof(int16_t));
codec.write(reinterpret_cast<uint8_t*>(pcm), frames * kChannels * sizeof(int16_t));
remaining -= static_cast<uint32_t>(frames);
}
};
for (size_t i = 0; i < (sizeof(kTone) / sizeof(kTone[0])); ++i)
{
const ToneStep& step = kTone[i];
uint32_t remaining = (kSampleRate * step.duration_ms) / 1000;
float phase = 0.0f;
const float phase_step = (kTwoPi * static_cast<float>(step.freq_hz)) / static_cast<float>(kSampleRate);
while (remaining > 0)
{
size_t frames = remaining > kFramesPerChunk ? kFramesPerChunk : static_cast<size_t>(remaining);
for (size_t n = 0; n < frames; ++n)
{
int16_t sample = static_cast<int16_t>(sinf(phase) * (32767.0f * kAmplitude));
pcm[n * 2] = sample;
pcm[n * 2 + 1] = sample;
phase += phase_step;
if (phase >= kTwoPi)
{
phase -= kTwoPi;
}
}
codec.write(reinterpret_cast<uint8_t*>(pcm), frames * kChannels * sizeof(int16_t));
remaining -= static_cast<uint32_t>(frames);
}
if (i + 1 < (sizeof(kTone) / sizeof(kTone[0])))
{
write_silence(kGapMs);
break;
}
codec.write(reinterpret_cast<uint8_t*>(pcm),
frames * pager_tone::kChannels * sizeof(int16_t));
delay(1);
}
codec.setVolume(static_cast<uint8_t>(prev_volume));
@@ -565,6 +565,8 @@ Important consequences:
- if firmware queues `FromRadio` data but does not cause the app to drain, status updates can appear delayed
- if firmware only wakes the app for some variants and not others, phone-side state may lag
- if `QueueStatus` or `ROUTING_APP` packets are generated but not drained, UI stays stale
- on nRF52/Bluefruit, the read-authorize callback must stay a lightweight consume marker; `FromRadio`
protobuf encoding, MQTT proxy polling, and characteristic preloading belong in the main runtime loop
## What Must Not Be Misinterpreted
+7 -4
View File
@@ -182,10 +182,13 @@ Stage 1 完成后:
1. `MeshtasticPhoneCore.notifyFromNum(from_num)` 把真实 `from_num` 交给 nRF52 transport
2. nRF52 transport 将 `from_num` 放入固定深度 pending 队列
3. 手机完成 `FROMNUM` 订阅后,transport 用同一个真实 `from_num` 发送通知
4. App 读取 `FROMRADIO` 时进入 `onFromRadioAuthorize()`
5. `writeNextFromRadioForRead()` 调用 `PhoneCore.popToPhone()` 产出下一帧并写入 `FROMRADIO`
6. App 持续读取,直到 `FROMRADIO` 返回空包,表示本轮 drain 完成
3. 主循环调用 `prepareReadableFromRadio()`,让 `PhoneCore.popToPhone()` 产出下一帧并预装进 `FROMRADIO`
4. 手机完成 `FROMNUM` 订阅且已有预装帧后,transport 用同一个真实 `from_num` 发送通知
5. App 读取 `FROMRADIO` 时进入 `onFromRadioAuthorize()`;该回调只记录读取/消费状态,不再产出 protobuf 帧
6. 主循环调用 `consumeReadableFromRadio()` 清空已读预装帧,再准备下一帧
7. App 持续读取,直到 `FROMRADIO` 返回空包,表示本轮 drain 完成
这里有一个稳定性边界:Bluefruit 的 read-authorize 回调不能执行 `popToPhone()`、MQTT proxy 轮询、nanopb 编码或串口日志重活。否则在手机高频 drain、空中包入站和 MQTT downlink 混在一起时,nRF52 侧可能出现无 HardFault 日志的 USB 重枚举/断连。
为了排障,目前固件还打印了这些日志:
@@ -0,0 +1,254 @@
#pragma once
#include "../../../../../../boards/t_echo_lite/include/boards/t_echo_lite/assets/pager_notification_adpcm.h"
#include <cstddef>
#include <cstdint>
namespace platform::ui::audio::pager_notification
{
namespace asset = ::boards::t_echo_lite::assets;
constexpr uint32_t kSourceSampleRateHz = asset::kPagerNotificationSampleRateHz;
constexpr uint32_t kPlaybackSampleRateHz = 44100U;
constexpr uint8_t kChannels = 2U;
static_assert(kPlaybackSampleRateHz % kSourceSampleRateHz == 0,
"Pager notification asset sample rate must divide the playback sample rate");
constexpr uint8_t kSampleRepeat =
static_cast<uint8_t>(kPlaybackSampleRateHz / kSourceSampleRateHz);
constexpr int8_t kImaAdpcmIndexTable[16] = {
-1,
-1,
-1,
-1,
2,
4,
6,
8,
-1,
-1,
-1,
-1,
2,
4,
6,
8,
};
constexpr int16_t kImaAdpcmStepTable[89] = {
7,
8,
9,
10,
11,
12,
13,
14,
16,
17,
19,
21,
23,
25,
28,
31,
34,
37,
41,
45,
50,
55,
60,
66,
73,
80,
88,
97,
107,
118,
130,
143,
157,
173,
190,
209,
230,
253,
279,
307,
337,
371,
408,
449,
494,
544,
598,
658,
724,
796,
876,
963,
1060,
1166,
1282,
1411,
1552,
1707,
1878,
2066,
2272,
2499,
2749,
3024,
3327,
3660,
4026,
4428,
4871,
5358,
5894,
6484,
7132,
7845,
8630,
9493,
10442,
11487,
12635,
13899,
15289,
16818,
18500,
20350,
22385,
24623,
27086,
29794,
32767,
};
struct AdpcmPlaybackState
{
int16_t predictor = asset::kPagerNotificationInitialSample;
int16_t current_sample = asset::kPagerNotificationInitialSample;
uint8_t step_index = 0U;
uint8_t repeat_remaining = 0U;
uint32_t sample_index = 0U;
};
inline bool hasMore(const AdpcmPlaybackState& state)
{
return state.sample_index < asset::kPagerNotificationSampleCount ||
state.repeat_remaining > 0U;
}
inline int16_t clampPcmSample(int32_t sample)
{
if (sample > 32767)
{
return 32767;
}
if (sample < -32768)
{
return -32768;
}
return static_cast<int16_t>(sample);
}
inline int16_t nextSourceSample(AdpcmPlaybackState& state)
{
if (state.sample_index == 0U)
{
state.sample_index = 1U;
return state.current_sample;
}
const uint32_t nibble_index = state.sample_index - 1U;
const uint8_t encoded = asset::kPagerNotificationAdpcmData[nibble_index / 2U];
const uint8_t code = (nibble_index & 1U) == 0U ? (encoded & 0x0FU) : (encoded >> 4);
const int16_t step = kImaAdpcmStepTable[state.step_index];
int32_t diff = step >> 3;
if ((code & 0x01U) != 0U)
{
diff += step >> 2;
}
if ((code & 0x02U) != 0U)
{
diff += step >> 1;
}
if ((code & 0x04U) != 0U)
{
diff += step;
}
const int32_t predictor = (code & 0x08U) != 0U
? static_cast<int32_t>(state.predictor) - diff
: static_cast<int32_t>(state.predictor) + diff;
state.predictor = clampPcmSample(predictor);
state.current_sample = state.predictor;
const int16_t next_index =
static_cast<int16_t>(state.step_index) + kImaAdpcmIndexTable[code & 0x0FU];
if (next_index < 0)
{
state.step_index = 0U;
}
else if (next_index > 88)
{
state.step_index = 88U;
}
else
{
state.step_index = static_cast<uint8_t>(next_index);
}
++state.sample_index;
return state.current_sample;
}
inline bool nextPlaybackSample(AdpcmPlaybackState& state, int16_t& sample)
{
if (!hasMore(state))
{
return false;
}
if (state.repeat_remaining == 0U)
{
if (state.sample_index >= asset::kPagerNotificationSampleCount)
{
return false;
}
state.current_sample = nextSourceSample(state);
state.repeat_remaining = kSampleRepeat;
}
sample = state.current_sample;
--state.repeat_remaining;
return true;
}
inline uint16_t fillStereoInterleaved(AdpcmPlaybackState& state,
int16_t* pcm,
uint16_t max_frames)
{
uint16_t frames = 0U;
while (frames < max_frames)
{
int16_t sample = 0;
if (!nextPlaybackSample(state, sample))
{
break;
}
pcm[frames * kChannels] = sample;
pcm[(frames * kChannels) + 1U] = sample;
++frames;
}
return frames;
}
} // namespace platform::ui::audio::pager_notification
@@ -16,6 +16,7 @@
#include "platform/shared/ble/phone_ble_runtime.h"
#include <atomic>
#include <bluefruit.h>
#include <cstdint>
#include <memory>
#include <string>
@@ -45,7 +46,7 @@ class MeshtasticBleService final : public BleService,
bool handleToRadio(const uint8_t* data, size_t len);
bool enqueueToRadio(const uint8_t* data, size_t len);
void flushPendingFromNumNotify();
bool writeNextFromRadioForRead();
void handleFromRadioReadRequest(uint16_t conn_handle, uint16_t offset, uintptr_t chr_ptr);
void handleConnectEvent(uint16_t conn_handle);
void handleDisconnectEvent(uint16_t conn_handle, uint8_t reason);
void handleFromNumCccdWrite(uint16_t conn_handle, uint16_t value);
@@ -73,6 +74,8 @@ class MeshtasticBleService final : public BleService,
void processPendingToRadio();
void processPendingPairingRequest();
void clearToPhoneQueue();
void prepareReadableFromRadio();
void consumeReadableFromRadio();
void syncMqttProxySettings();
void markConfigSavePending(bool bluetooth_changed, bool module_changed);
void flushPendingConfigSaves(bool force = false);
@@ -120,12 +123,16 @@ class MeshtasticBleService final : public BleService,
volatile uint16_t pending_pairing_conn_handle_ = BLE_CONN_HANDLE_INVALID;
phone::meshtastic::MeshtasticBleFrame session_frame_scratch_{};
phone::meshtastic::MeshtasticBleFrame from_radio_preloaded_{};
volatile bool from_radio_preloaded_valid_ = false;
volatile bool from_radio_consume_pending_ = false;
volatile bool pending_connect_log_ = false;
volatile bool pending_disconnect_log_ = false;
volatile bool pending_from_num_cccd_log_ = false;
volatile bool pending_pair_complete_log_ = false;
volatile bool pending_secured_log_ = false;
volatile bool pending_from_radio_auth_log_ = false;
volatile uint16_t pending_connect_conn_handle_ = BLE_CONN_HANDLE_INVALID;
volatile uint16_t pending_disconnect_conn_handle_ = BLE_CONN_HANDLE_INVALID;
@@ -135,6 +142,10 @@ class MeshtasticBleService final : public BleService,
volatile uint16_t pending_pair_complete_conn_handle_ = BLE_CONN_HANDLE_INVALID;
volatile uint8_t pending_pair_complete_status_ = 0;
volatile uint16_t pending_secured_conn_handle_ = BLE_CONN_HANDLE_INVALID;
volatile uint16_t pending_from_radio_auth_conn_handle_ = BLE_CONN_HANDLE_INVALID;
volatile uint16_t pending_from_radio_auth_offset_ = 0;
volatile uintptr_t pending_from_radio_auth_chr_ = 0;
volatile uintptr_t pending_from_radio_auth_svc_ = 0;
volatile bool pending_from_radio_read_log_ = false;
volatile bool pending_from_radio_empty_log_ = false;
@@ -372,18 +372,10 @@ void onFromRadioAuthorize(uint16_t conn_handle, BLECharacteristic* chr, ble_gatt
return;
}
bleLogBoth("[BLE][nrf52][mt][auth] conn=%u offset=%u chr=%p svc=%p",
static_cast<unsigned>(conn_handle),
static_cast<unsigned>(request->offset),
static_cast<void*>(chr),
static_cast<void*>(s_active_service));
if (request->offset == 0)
if (s_active_service)
{
if (s_active_service)
{
(void)s_active_service->writeNextFromRadioForRead();
}
s_active_service->handleFromRadioReadRequest(
conn_handle, request->offset, reinterpret_cast<uintptr_t>(chr));
}
authorizeRead(conn_handle);
@@ -474,8 +466,13 @@ void MeshtasticBleService::start()
pending_from_num_cccd_log_ = false;
pending_pair_complete_log_ = false;
pending_secured_log_ = false;
pending_from_radio_auth_log_ = false;
pending_from_radio_read_log_ = false;
pending_from_radio_empty_log_ = false;
pending_from_radio_auth_conn_handle_ = BLE_CONN_HANDLE_INVALID;
pending_from_radio_auth_offset_ = 0;
pending_from_radio_auth_chr_ = 0;
pending_from_radio_auth_svc_ = 0;
pending_from_radio_empty_reason_ = 0;
pending_disconnect_reason_ = 0;
pairing_request_pending_ = false;
@@ -566,8 +563,13 @@ void MeshtasticBleService::stop()
pending_from_num_cccd_log_ = false;
pending_pair_complete_log_ = false;
pending_secured_log_ = false;
pending_from_radio_auth_log_ = false;
pending_from_radio_read_log_ = false;
pending_from_radio_empty_log_ = false;
pending_from_radio_auth_conn_handle_ = BLE_CONN_HANDLE_INVALID;
pending_from_radio_auth_offset_ = 0;
pending_from_radio_auth_chr_ = 0;
pending_from_radio_auth_svc_ = 0;
pending_from_radio_empty_reason_ = 0;
pending_disconnect_reason_ = 0;
pairing_request_pending_ = false;
@@ -598,6 +600,11 @@ void MeshtasticBleService::update()
processPendingPairingRequest();
processPendingToRadio();
if (from_radio_consume_pending_)
{
consumeReadableFromRadio();
}
prepareReadableFromRadio();
flushPendingFromNumNotify();
logDeferredBleEvents();
flushPendingConfigSaves(false);
@@ -738,25 +745,51 @@ bool MeshtasticBleService::handleToRadio(const uint8_t* data, size_t len)
return ok;
}
bool MeshtasticBleService::writeNextFromRadioForRead()
void MeshtasticBleService::handleFromRadioReadRequest(uint16_t conn_handle,
uint16_t offset,
uintptr_t chr_ptr)
{
uint8_t empty = 0;
pending_from_radio_auth_conn_handle_ = conn_handle;
pending_from_radio_auth_offset_ = offset;
pending_from_radio_auth_chr_ = chr_ptr;
pending_from_radio_auth_svc_ = reinterpret_cast<uintptr_t>(this);
pending_from_radio_auth_log_ = true;
if (offset != 0)
{
return;
}
last_ble_activity_ms_ = millis();
if (!active_ || !connected_ || !phone_session_)
{
from_radio_.write(&empty, 0);
pending_from_radio_empty_log_ = true;
pending_from_radio_empty_reason_ = kFromRadioEmptyInactive;
return false;
return;
}
if (!from_radio_preloaded_valid_)
{
pending_from_radio_empty_log_ = true;
pending_from_radio_empty_reason_ = kFromRadioEmptyNoFrame;
return;
}
from_radio_consume_pending_ = true;
}
void MeshtasticBleService::prepareReadableFromRadio()
{
if (from_radio_preloaded_valid_ || !active_ || !connected_ || !phone_session_)
{
return;
}
auto& session_frame = session_frame_scratch_;
std::memset(&session_frame, 0, sizeof(session_frame));
if (!phone_session_->popToPhone(&session_frame))
{
from_radio_.write(&empty, 0);
pending_from_radio_empty_log_ = true;
pending_from_radio_empty_reason_ = kFromRadioEmptyNoFrame;
return false;
return;
}
if (session_frame.len == 0 || session_frame.len > meshtastic_FromRadio_size)
@@ -765,27 +798,42 @@ bool MeshtasticBleService::writeNextFromRadioForRead()
static_cast<unsigned long>(session_frame.from_num),
static_cast<unsigned>(session_frame.len),
static_cast<unsigned>(meshtastic_FromRadio_size));
uint8_t empty = 0;
from_radio_.write(&empty, 0);
pending_from_radio_empty_log_ = true;
pending_from_radio_empty_reason_ = kFromRadioEmptyInvalidFrame;
return false;
return;
}
from_radio_.write(session_frame.buf, session_frame.len);
pending_from_radio_read_len_ = static_cast<uint16_t>(session_frame.len);
pending_from_radio_read_from_num_ = session_frame.from_num;
pending_from_radio_read_log_ = true;
if (pending_from_num_count_ > 0 &&
pending_from_num_[pending_from_num_head_] == session_frame.from_num)
from_radio_preloaded_ = session_frame;
from_radio_.write(from_radio_preloaded_.buf, from_radio_preloaded_.len);
from_radio_preloaded_valid_ = true;
bleLogBoth("[BLE][nrf52][mt][flow] from_radio preload from_num=%08lX len=%u pending_from_num=%u",
static_cast<unsigned long>(from_radio_preloaded_.from_num),
static_cast<unsigned>(from_radio_preloaded_.len),
static_cast<unsigned>(pending_from_num_count_));
}
void MeshtasticBleService::consumeReadableFromRadio()
{
from_radio_consume_pending_ = false;
if (!from_radio_preloaded_valid_)
{
pending_from_num_head_ =
static_cast<uint8_t>((pending_from_num_head_ + 1U) % kPendingFromNumCapacity);
--pending_from_num_count_;
pending_from_radio_empty_log_ = true;
pending_from_radio_empty_reason_ = kFromRadioEmptyNoFrame;
return;
}
bleLogBoth("[BLE][nrf52][mt][flow] from_radio authorize frame from_num=%08lX len=%u",
static_cast<unsigned long>(session_frame.from_num),
static_cast<unsigned>(session_frame.len));
return true;
pending_from_radio_read_len_ = static_cast<uint16_t>(from_radio_preloaded_.len);
pending_from_radio_read_from_num_ = from_radio_preloaded_.from_num;
pending_from_radio_read_log_ = true;
from_radio_preloaded_ = phone::meshtastic::MeshtasticBleFrame{};
from_radio_preloaded_valid_ = false;
uint8_t empty = 0;
from_radio_.write(&empty, 0);
}
bool MeshtasticBleService::enqueueToRadio(const uint8_t* data, size_t len)
@@ -852,6 +900,9 @@ void MeshtasticBleService::clearToPhoneQueue()
pending_from_num_tail_ = 0;
pending_from_num_count_ = 0;
session_frame_scratch_ = phone::meshtastic::MeshtasticBleFrame{};
from_radio_preloaded_ = phone::meshtastic::MeshtasticBleFrame{};
from_radio_preloaded_valid_ = false;
from_radio_consume_pending_ = false;
}
void MeshtasticBleService::loadRememberedPhonePeer()
@@ -1228,6 +1279,11 @@ void MeshtasticBleService::flushPendingFromNumNotify()
return;
}
if (!from_radio_preloaded_valid_)
{
return;
}
from_num_.write32(from_num);
const bool ok = from_num_.notify32(conn_handle_, from_num);
bleLogBoth("[BLE][nrf52][mt][flow] from_num notify value=%08lX source=%08lX conn=%u ok=%u cccd=0x%04X",
@@ -1435,6 +1491,16 @@ void MeshtasticBleService::logDeferredBleEvents()
static_cast<unsigned>(pending_secured_conn_handle_));
}
if (pending_from_radio_auth_log_)
{
pending_from_radio_auth_log_ = false;
bleLogBoth("[BLE][nrf52][mt][auth] conn=%u offset=%u chr=%p svc=%p",
static_cast<unsigned>(pending_from_radio_auth_conn_handle_),
static_cast<unsigned>(pending_from_radio_auth_offset_),
reinterpret_cast<void*>(static_cast<uintptr_t>(pending_from_radio_auth_chr_)),
reinterpret_cast<void*>(static_cast<uintptr_t>(pending_from_radio_auth_svc_)));
}
if (pending_from_radio_read_log_)
{
pending_from_radio_read_log_ = false;