Skip to content

Commit 42b3c8f

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

3 files changed

Lines changed: 114 additions & 297 deletions

File tree

frontend/src/bleclient.js

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ const FRAGMENT_HEADER_SIZE = 5;
3838
const DEFAULT_TIMEOUT_MS = 50000;
3939
const MAX_RETRIES = 3;
4040

41+
// Verbose logging (set to false for production)
42+
const VERBOSE_LOGGING = false;
43+
4144
export class BLEClient {
4245
constructor() {
4346
this.device = null;
@@ -68,21 +71,40 @@ export class BLEClient {
6871
this.onDisconnectCallback = null;
6972
}
7073

74+
// Logging helpers
75+
logVerbose(...args) {
76+
if (VERBOSE_LOGGING) {
77+
console.log('[BLE VERBOSE]', ...args);
78+
}
79+
}
80+
81+
logInfo(...args) {
82+
console.log('[BLE]', ...args);
83+
}
84+
85+
logWarn(...args) {
86+
console.warn('[BLE]', ...args);
87+
}
88+
89+
logError(...args) {
90+
console.error('[BLE]', ...args);
91+
}
92+
7193
/**
7294
* Establish connection to BLE device
7395
* @returns {Promise<void>}
7496
*/
7597
async connect() {
7698
try {
77-
console.log('Starting BLE connection...');
99+
this.logInfo('Starting BLE connection...');
78100

79101
// Select device
80102
this.device = await navigator.bluetooth.requestDevice({
81103
filters : [ {services : [ SERVICE_UUID ]} ],
82104
optionalServices : [ SERVICE_UUID ]
83105
});
84106

85-
console.log('Device selected:', this.device.name);
107+
this.logInfo('Device selected:', this.device.name);
86108

87109
// Disconnect handler
88110
this.device.addEventListener('gattserverdisconnected', () => {
@@ -91,26 +113,27 @@ export class BLEClient {
91113

92114
// Connect to GATT server
93115
this.server = await this.device.gatt.connect();
94-
console.log('GATT server connected');
116+
this.logVerbose('GATT server connected');
95117

96118
// Get service
97119
this.service = await this.server.getPrimaryService(SERVICE_UUID);
98-
console.log('Service found');
120+
this.logVerbose('Service found');
99121

100122
// Get characteristics
101123
this.requestChar = await this.service.getCharacteristic(REQUEST_CHAR_UUID);
102124
this.responseChar = await this.service.getCharacteristic(RESPONSE_CHAR_UUID);
103125
this.eventChar = await this.service.getCharacteristic(EVENT_CHAR_UUID);
104126
this.controlChar = await this.service.getCharacteristic(CONTROL_CHAR_UUID);
105127

106-
console.log('All characteristics found');
128+
this.logVerbose('All characteristics found');
107129

108130
// Enable notifications
109131
await this.responseChar.startNotifications();
110132
await this.eventChar.startNotifications();
111133
await this.controlChar.startNotifications();
112134

113135
// Event listeners for incoming data
136+
this.logVerbose('Registering event listeners for characteristics');
114137
this.responseChar.addEventListener('characteristicvaluechanged',
115138
(event) => this.handleFragment(event.target.value, 'response'));
116139

@@ -121,14 +144,14 @@ export class BLEClient {
121144
(event) => this.handleControlMessage(event.target.value));
122145

123146
this.connected = true;
124-
console.log('BLE connection successfully established');
147+
this.logInfo('BLE connection successfully established');
125148

126149
// Wait for MTU information from server (with timeout)
127150
await this.waitForMTU(2000);
128-
console.log(`MTU: ${this.mtu} bytes (Payload: ${this.mtu - 3} bytes)`);
151+
this.logInfo(`MTU: ${this.mtu} bytes (Payload: ${this.mtu - 3} bytes)`);
129152

130153
} catch (error) {
131-
console.error('Connection error:', error);
154+
this.logError('Connection error:', error);
132155
throw new Error(`BLE connection failed: ${error.message}`);
133156
}
134157
}
@@ -138,7 +161,7 @@ export class BLEClient {
138161
* @param {number} timeout - Timeout in milliseconds
139162
*/
140163
async waitForMTU(timeout = 2000) {
141-
console.log('Waiting for MTU information from server...');
164+
this.logVerbose('Waiting for MTU information from server...');
142165

143166
// Create promise for MTU reception
144167
const mtuPromise = new Promise((resolve) => {
@@ -155,10 +178,10 @@ export class BLEClient {
155178

156179
// If timeout, determine MTU ourselves
157180
if (result === 'timeout') {
158-
console.warn('No MTU info received from server (timeout), determining ourselves...');
181+
this.logWarn('No MTU info received from server (timeout), determining MTU ourselves...');
159182
await this.negotiateMTU();
160183
} else {
161-
console.log('MTU info from server successfully received');
184+
this.logInfo('MTU info from server successfully received');
162185
}
163186
}
164187

@@ -174,15 +197,15 @@ export class BLEClient {
174197
testData.fill(0xFF);
175198
await this.requestChar.writeValue(testData);
176199
this.mtu = size;
177-
console.log(`MTU test successful: ${size} bytes`);
200+
this.logInfo(`MTU negotiated: ${size} bytes`);
178201
return;
179202
} catch (error) {
180203
continue;
181204
}
182205
}
183206

184207
this.mtu = 23;
185-
console.warn('MTU negotiation failed, using minimum: 23 bytes');
208+
this.logWarn('MTU negotiation failed, using minimum: 23 bytes');
186209
}
187210

188211
/**
@@ -199,7 +222,7 @@ export class BLEClient {
199222
* Disconnect handler
200223
*/
201224
handleDisconnect() {
202-
console.log('Connection closed');
225+
this.logInfo('Disconnected');
203226
this.connected = false;
204227

205228
for (const [messageId, request] of this.pendingRequests) {
@@ -228,7 +251,7 @@ export class BLEClient {
228251
*/
229252
handleFragment(dataView, type) {
230253
if (dataView.byteLength < FRAGMENT_HEADER_SIZE) {
231-
console.error('Fragment too small');
254+
this.logError('Fragment too small');
232255
return;
233256
}
234257

@@ -238,7 +261,7 @@ export class BLEClient {
238261
const fragmentNum = dataView.getUint16(2);
239262
const flags = dataView.getUint8(4);
240263

241-
console.log(`Fragment received: ProtocolType=${protocolMsgType}, ID=${messageId}, Num=${fragmentNum}, Flags=0x${flags.toString(16)}`);
264+
this.logVerbose(`Fragment received: ProtocolType=${protocolMsgType}, ID=${messageId}, Num=${fragmentNum}, Flags=0x${flags.toString(16)}`);
242265

243266
// Extract payload
244267
const payload = new Uint8Array(
@@ -265,7 +288,7 @@ export class BLEClient {
265288

266289
// Last fragment?
267290
if (flags & FLAG_LAST_FRAGMENT) {
268-
console.log(`Complete message received: ID=${messageId}, Fragments=${buffer.fragments.length}`);
291+
this.logVerbose(`Complete message received: ID=${messageId}, Fragments=${buffer.fragments.length}`);
269292

270293
// Merge fragments
271294
const completeData = this.mergeFragments(buffer.fragments);
@@ -313,7 +336,7 @@ export class BLEClient {
313336
// Return complete data (incl. request type if server sends it back)
314337
pending.resolve(data);
315338
} else {
316-
console.warn(`No pending request for message ID ${messageId}`);
339+
this.logWarn(`No pending request for message ID ${messageId}`);
317340
}
318341
}
319342

@@ -324,15 +347,15 @@ export class BLEClient {
324347
*/
325348
handleEvent(messageId, data) {
326349
if (data.length === 0) {
327-
console.warn('Event received without data');
350+
this.logWarn('Event received without data');
328351
return;
329352
}
330353

331354
// First byte is the Application Event Type
332355
const appEventType = data[0];
333356
const eventData = data.slice(1);
334357

335-
console.log(`Event received: AppEventType=${appEventType}, Size=${eventData.length}`);
358+
this.logVerbose(`Event received: AppEventType=${appEventType}, Size=${eventData.length}`);
336359

337360
// Call event listeners
338361
const listeners = this.eventListeners.get(appEventType);
@@ -341,7 +364,7 @@ export class BLEClient {
341364
try {
342365
listener(eventData);
343366
} catch (error) {
344-
console.error('Error in event listener:', error);
367+
this.logError('Error in event listener:', error);
345368
}
346369
}
347370
}
@@ -353,7 +376,7 @@ export class BLEClient {
353376
try {
354377
listener(appEventType, eventData);
355378
} catch (error) {
356-
console.error('Error in wildcard event listener:', error);
379+
this.logError('Error in wildcard event listener:', error);
357380
}
358381
}
359382
}
@@ -369,13 +392,13 @@ export class BLEClient {
369392

370393
const ctrlType = dataView.getUint8(0);
371394

372-
console.log(`Control message received: Type=${ctrlType}, Length=${dataView.byteLength}`);
395+
this.logVerbose(`Control message received: Type=${ctrlType}, Length=${dataView.byteLength}`);
373396

374397
switch (ctrlType) {
375398
case ControlMessageType.MTU_INFO:
376399
if (dataView.byteLength >= 4) {
377400
const mtu = (dataView.getUint8(2) << 8) | dataView.getUint8(3);
378-
console.log(`MTU info from server: ${mtu} bytes (payload: ${mtu - 3} bytes)`);
401+
this.logInfo(`MTU received from server: ${mtu} bytes (payload: ${mtu - 3} bytes)`);
379402
this.mtu = mtu;
380403
this.mtuReceived = true;
381404

@@ -384,21 +407,21 @@ export class BLEClient {
384407
this.mtuReceivedResolve = null;
385408
}
386409
} else {
387-
console.error('MTU_INFO too short:', dataView.byteLength);
410+
this.logError('MTU_INFO too short:', dataView.byteLength);
388411
}
389412
break;
390413

391414
case ControlMessageType.ACK:
392415
if (dataView.byteLength >= 2) {
393416
const messageId = dataView.getUint8(1);
394-
console.log(`ACK for message ${messageId}`);
417+
this.logVerbose(`ACK for message ${messageId}`);
395418
}
396419
break;
397420

398421
case ControlMessageType.NACK:
399422
if (dataView.byteLength >= 2) {
400423
const messageId = dataView.getUint8(1);
401-
console.warn(`NACK for message ${messageId}`);
424+
this.logWarn(`NACK received for message ${messageId}`);
402425

403426
const pending = this.pendingRequests.get(messageId);
404427
if (pending) {
@@ -412,7 +435,7 @@ export class BLEClient {
412435
case ControlMessageType.BUFFER_FULL:
413436
if (dataView.byteLength >= 2) {
414437
const messageId = dataView.getUint8(1);
415-
console.error(`Buffer full for message ${messageId}`);
438+
this.logWarn(`Buffer full for message ${messageId}`);
416439

417440
const pending = this.pendingRequests.get(messageId);
418441
if (pending) {
@@ -426,17 +449,17 @@ export class BLEClient {
426449
case ControlMessageType.RETRY:
427450
if (dataView.byteLength >= 2) {
428451
const messageId = dataView.getUint8(1);
429-
console.log(`Server requests retry for message ${messageId}`);
452+
this.logVerbose(`Server requests retry for message ${messageId}`);
430453
}
431454
break;
432455

433456
case ControlMessageType.RESET:
434-
console.log('Server requests reset');
457+
this.logInfo('Server requests reset');
435458
this.fragmentBuffers.clear();
436459
break;
437460

438461
default:
439-
console.warn(`Unknown control type: ${ctrlType}`);
462+
this.logWarn(`Unknown control message type: ${ctrlType}`);
440463
}
441464
}
442465

@@ -451,7 +474,7 @@ export class BLEClient {
451474
const payloadSize = this.mtu - FRAGMENT_HEADER_SIZE;
452475
const totalFragments = Math.ceil(data.length / payloadSize) || 1;
453476

454-
console.log(`Sending fragmented: ProtocolType=${protocolMsgType}, ID=${messageId}, Size=${data.length}, Fragments=${totalFragments}`);
477+
this.logVerbose(`Sending fragmented: ProtocolType=${protocolMsgType}, ID=${messageId}, Size=${data.length}, Fragments=${totalFragments}`);
455478

456479
for (let i = 0; i < totalFragments; i++) {
457480
const fragment = new Uint8Array(Math.min(this.mtu, FRAGMENT_HEADER_SIZE + data.length - i * payloadSize));
@@ -472,13 +495,9 @@ export class BLEClient {
472495
const payload = data.slice(start, end);
473496
fragment.set(payload, FRAGMENT_HEADER_SIZE);
474497

475-
// Send fragment
476-
await characteristic.writeValue(fragment.slice(0, FRAGMENT_HEADER_SIZE + payload.length));
477-
478-
// Small pause between fragments
479-
if (i < totalFragments - 1) {
480-
await this.sleep(10);
481-
}
498+
// Send fragment with ATT Write Response (BLE flow control)
499+
// This waits for ESP32 confirmation before sending next fragment
500+
await characteristic.writeValueWithResponse(fragment.slice(0, FRAGMENT_HEADER_SIZE + payload.length));
482501
}
483502
}
484503

@@ -553,15 +572,15 @@ export class BLEClient {
553572

554573
for (let attempt = 0; attempt < maxRetries; attempt++) {
555574
try {
556-
console.log(`Request attempt ${attempt + 1}/${maxRetries}`);
575+
this.logInfo(`Request attempt ${attempt + 1}/${maxRetries}`);
557576
return await this.sendRequest(appRequestType, data, timeout);
558577
} catch (error) {
559578
lastError = error;
560-
console.warn(`Request failed (attempt ${attempt + 1}):`, error.message);
579+
this.logWarn(`Request failed (attempt ${attempt + 1}):`, error.message);
561580

562581
if (attempt < maxRetries - 1) {
563582
const delay = 1000 * Math.pow(2, attempt);
564-
console.warn(`Waiting ${delay}ms before retry...`);
583+
this.logWarn(`Waiting ${delay}ms before retry...`);
565584
await this.sleep(delay);
566585
}
567586
}
@@ -581,7 +600,7 @@ export class BLEClient {
581600
}
582601
this.eventListeners.get(appEventType).push(callback);
583602

584-
console.log(`Event listener registered for AppEventType: ${appEventType}`);
603+
this.logVerbose(`Event listener registered for AppEventType: ${appEventType}`);
585604
}
586605

587606
/**

0 commit comments

Comments
 (0)