Skip to content

Commit 2a536f3

Browse files
committed
Working on BT/BLE API
1 parent f7115e9 commit 2a536f3

2 files changed

Lines changed: 93 additions & 20 deletions

File tree

lib/btremote/include/btremote.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ class BTRemote {
9898
QueueHandle_t responseQueue_;
9999
TaskHandle_t responseSenderTaskHandle_;
100100

101+
// FreeRTOS synchronization for indication confirmation
102+
SemaphoreHandle_t indicationConfirmSemaphore_;
103+
101104
// Callback-Funktionen
102105
// Callback: (appRequestType, messageId, payload)
103106
std::function<void(uint8_t, uint8_t, const std::vector<uint8_t> &)> onRequestCallback_;
@@ -153,6 +156,7 @@ class BTRemote {
153156
void handleGattsCreate(esp_ble_gatts_cb_param_t *param);
154157
void handleGattsAddChar(esp_ble_gatts_cb_param_t *param);
155158
void handleGattsAddCharDescr(esp_ble_gatts_cb_param_t *param);
159+
void handleGattsConfirm(esp_ble_gatts_cb_param_t *param);
156160

157161
public:
158162
BTRemote(FS *fs, Megahub *hub, SerialLoggingOutput *loggingOutput, Configuration *configuragtion);

lib/btremote/src/btremote.cpp

Lines changed: 89 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,6 @@ static esp_ble_adv_params_t g_adv_params = {
9191
#define APP_EVENT_TYPE_PORTSTATUS 0x02
9292
#define APP_EVENT_TYPE_COMMAND 0x03
9393

94-
// Delay zwischen Fragmenten (in ms)
95-
// Small delay to allow browser's JS event loop to process events
96-
// Much lower now that we're not blocking in callback context
97-
const uint32_t INTER_FRAGMENT_DELAY_MS = 200;
98-
9994
// Task-Loop-Delay (in ms) - CPU-Zeit freigeben
10095
const uint32_t TASK_LOOP_DELAY_MS = 10;
10196

@@ -248,7 +243,8 @@ inline bool parseJsonFromVector(const std::vector<uint8_t> &buffer, Func fn) {
248243
// Message types for the unified message processor queue
249244
enum class MessageProcessorItemType : uint8_t {
250245
INCOMING_REQUEST, // Process incoming request (was blocking callback)
251-
FILE_TRANSFER // Send file response (existing functionality)
246+
FILE_TRANSFER, // Send file response (existing functionality)
247+
MTU_NOTIFICATION // Send MTU notification (from GATTS callback)
252248
};
253249

254250
struct PendingFileTransfer {
@@ -285,6 +281,13 @@ struct MessageProcessorItem {
285281
item.dataPtr = new IncomingRequest{protocolType, messageId, data};
286282
return item;
287283
}
284+
285+
static MessageProcessorItem createMTUNotification() {
286+
MessageProcessorItem item;
287+
item.type = MessageProcessorItemType::MTU_NOTIFICATION;
288+
item.dataPtr = nullptr; // No data needed, MTU is stored in class member
289+
return item;
290+
}
288291
};
289292

290293
// Unified message processor task - handles requests, file transfers, and event publishing
@@ -307,11 +310,16 @@ BTRemote::BTRemote(FS *fs, Megahub *hub, SerialLoggingOutput *loggingOutput, Con
307310
, mtu_(23)
308311
, nextMessageId_(0)
309312
, responseQueue_(nullptr)
310-
, responseSenderTaskHandle_(nullptr) {
313+
, responseSenderTaskHandle_(nullptr)
314+
, indicationConfirmSemaphore_(nullptr) {
311315
// Create unified message processor queue (handles both requests and file transfers)
312316
// Increased to 5 to handle concurrent requests + file transfers
313317
responseQueue_ = xQueueCreate(5, sizeof(MessageProcessorItem));
314318

319+
// Create binary semaphore for indication confirmation flow control
320+
// Binary semaphore is used to wait for ESP_GATTS_CONF_EVT after each indication
321+
indicationConfirmSemaphore_ = xSemaphoreCreateBinary();
322+
315323
// Initialize handles to invalid values
316324
memset(&handles_, 0, sizeof(handles_));
317325
memset(&connState_, 0, sizeof(connState_));
@@ -600,6 +608,10 @@ bool BTRemote::sendLargeResponse(uint8_t messageId, size_t totalSize, std::funct
600608
int read = chunkProvider(i, payloadSize, fragment);
601609
DEBUG("Read %d bytes for chunk with index %d, fragment size is %d, flags is %d", read, i, fragment.size(), flags);
602610

611+
// Clear semaphore before sending to handle race condition where
612+
// confirmation arrives before we call xSemaphoreTake()
613+
xSemaphoreTake(indicationConfirmSemaphore_, 0);
614+
603615
// Send fragment using Bluedroid indicate
604616
esp_err_t ret = esp_ble_gatts_send_indicate(gatts_if_, connState_.conn_id,
605617
handles_.response_char_handle, fragment.size(), fragment.data(), true); // true = need confirm (indicate)
@@ -609,9 +621,16 @@ bool BTRemote::sendLargeResponse(uint8_t messageId, size_t totalSize, std::funct
609621
return false;
610622
}
611623

612-
// Delay between fragments for browser JS event loop
613-
if (i < totalFragments - 1) {
614-
vTaskDelay(pdMS_TO_TICKS(INTER_FRAGMENT_DELAY_MS));
624+
DEBUG("Indicate sent, waiting for confirmation for fragment %d/%d", i + 1, totalFragments);
625+
626+
// Wait for indication confirmation (ESP_GATTS_CONF_EVT) with timeout
627+
// Using 1000ms timeout to handle poor RF conditions
628+
if (xSemaphoreTake(indicationConfirmSemaphore_, pdMS_TO_TICKS(1000)) == pdTRUE) {
629+
DEBUG("Confirmation received for fragment %d/%d", i + 1, totalFragments);
630+
} else {
631+
WARN("Confirmation timeout for fragment %d of %d", i + 1, totalFragments);
632+
// Could implement retry logic here, but for now just fail
633+
return false;
615634
}
616635
}
617636

@@ -659,6 +678,10 @@ bool BTRemote::sendFragmented(uint16_t char_handle, ProtocolMessageType protocol
659678
fragment.insert(fragment.end(), data.begin() + start, data.begin() + end);
660679
}
661680

681+
// Clear semaphore before sending to handle race condition where
682+
// confirmation arrives before we call xSemaphoreTake()
683+
xSemaphoreTake(indicationConfirmSemaphore_, 0);
684+
662685
// Send fragment using Bluedroid indicate
663686
DEBUG("Sending indicate: gatts_if=%d, conn_id=%d, handle=%d, len=%d, protocolType=%d, msgId=%d",
664687
gatts_if_, connState_.conn_id, char_handle, fragment.size(), (uint8_t)protocolType, messageId);
@@ -669,13 +692,18 @@ bool BTRemote::sendFragmented(uint16_t char_handle, ProtocolMessageType protocol
669692
if (ret != ESP_OK) {
670693
ERROR("Failed to indicate fragment %d of %d: %s", i + 1, totalFragments, esp_err_to_name(ret));
671694
return false;
672-
} else {
673-
DEBUG("Indicate sent successfully for fragment %d/%d", i + 1, totalFragments);
674695
}
675696

676-
// Delay between fragments for browser JS event loop
677-
if (i < totalFragments - 1) {
678-
vTaskDelay(pdMS_TO_TICKS(INTER_FRAGMENT_DELAY_MS));
697+
DEBUG("Indicate sent, waiting for confirmation for fragment %d/%d", i + 1, totalFragments);
698+
699+
// Wait for indication confirmation (ESP_GATTS_CONF_EVT) with timeout
700+
// Using 1000ms timeout to handle poor RF conditions
701+
if (xSemaphoreTake(indicationConfirmSemaphore_, pdMS_TO_TICKS(1000)) == pdTRUE) {
702+
DEBUG("Confirmation received for fragment %d/%d", i + 1, totalFragments);
703+
} else {
704+
WARN("Confirmation timeout for fragment %d of %d", i + 1, totalFragments);
705+
// Could implement retry logic here, but for now just fail
706+
return false;
679707
}
680708
}
681709

@@ -890,13 +918,19 @@ void BTRemote::processMessageQueue() {
890918
// CRITICAL: Manually free the allocated memory
891919
// (No destructor on MessageProcessorItem because FreeRTOS uses memcpy)
892920
delete transfer;
921+
922+
} else if (item.type == MessageProcessorItemType::MTU_NOTIFICATION) {
923+
// Send MTU notification (called from task context, not callback)
924+
INFO("Processing MTU notification from task context");
925+
sendMTUNotification();
926+
// No memory to free, dataPtr is nullptr
893927
}
894928
}
895929

896930
// After processing (or timeout), check for pending events and send them
897931
// This prevents race conditions - all BLE communication is serialized through this task
898932
if (isConnected() && readyForEvents_) {
899-
//publishLogMessages();
933+
publishLogMessages();
900934
publishCommands();
901935
publishPortstatus();
902936
}
@@ -1009,6 +1043,9 @@ void BTRemote::gattsEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts
10091043
case ESP_GATTS_MTU_EVT:
10101044
g_btremote_instance->handleGattsMTU(param);
10111045
break;
1046+
case ESP_GATTS_CONF_EVT:
1047+
g_btremote_instance->handleGattsConfirm(param);
1048+
break;
10121049
default:
10131050
break;
10141051
}
@@ -1282,9 +1319,12 @@ void BTRemote::handleGattsConnect(esp_ble_gatts_cb_param_t *param) {
12821319
connState_.remote_addr[2], connState_.remote_addr[3],
12831320
connState_.remote_addr[4], connState_.remote_addr[5]);
12841321

1285-
// Small delay for stability
1286-
vTaskDelay(pdMS_TO_TICKS(100));
1287-
sendMTUNotification();
1322+
// Queue MTU notification to be sent from task context (not callback)
1323+
// This avoids calling vTaskDelay from ISR context
1324+
MessageProcessorItem item = MessageProcessorItem::createMTUNotification();
1325+
if (xQueueSend(responseQueue_, &item, 0) != pdTRUE) {
1326+
WARN("Failed to queue MTU notification");
1327+
}
12881328
}
12891329

12901330
void BTRemote::handleGattsDisconnect(esp_ble_gatts_cb_param_t *param) {
@@ -1303,7 +1343,36 @@ void BTRemote::handleGattsMTU(esp_ble_gatts_cb_param_t *param) {
13031343
mtu_ = param->mtu.mtu - 3; // Subtract ATT header
13041344
connState_.mtu = mtu_;
13051345
INFO("MTU changed to: %d (notified with %d)", mtu_, param->mtu.mtu);
1306-
sendMTUNotification();
1346+
1347+
// Queue MTU notification to be sent from task context (not callback)
1348+
MessageProcessorItem item = MessageProcessorItem::createMTUNotification();
1349+
if (xQueueSend(responseQueue_, &item, 0) != pdTRUE) {
1350+
WARN("Failed to queue MTU notification");
1351+
}
1352+
}
1353+
1354+
void BTRemote::handleGattsConfirm(esp_ble_gatts_cb_param_t *param) {
1355+
// Indication confirmed by client - signal the waiting task
1356+
DEBUG("Indication confirmed: status=%d, handle=%d", param->conf.status, param->conf.handle);
1357+
1358+
if (param->conf.status == ESP_GATT_OK) {
1359+
// Give semaphore to unblock the sending task
1360+
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
1361+
xSemaphoreGiveFromISR(indicationConfirmSemaphore_, &xHigherPriorityTaskWoken);
1362+
1363+
// Yield to higher priority task if needed
1364+
if (xHigherPriorityTaskWoken) {
1365+
portYIELD_FROM_ISR();
1366+
}
1367+
} else {
1368+
WARN("Indication confirmation failed: status=%d", param->conf.status);
1369+
// Still signal to prevent deadlock, sender will handle error
1370+
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
1371+
xSemaphoreGiveFromISR(indicationConfirmSemaphore_, &xHigherPriorityTaskWoken);
1372+
if (xHigherPriorityTaskWoken) {
1373+
portYIELD_FROM_ISR();
1374+
}
1375+
}
13071376
}
13081377

13091378
void BTRemote::handleGattsWrite(esp_ble_gatts_cb_param_t *param) {

0 commit comments

Comments
 (0)