mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-10-22 21:22:14 +00:00
Stabilize nRF BLE reads and share pager tone
This commit is contained in:
1 parent
8540fe256b
commit
556a50dac7
8 files changed
+447
-118
No files matched your search
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user