From 1d3a7eba45270c0c8342abb83d488ab6a0fec316 Mon Sep 17 00:00:00 2001 From: Yudi Xiao <463708580@qq.com> Date: Wed, 10 Dec 2025 20:16:05 +0800 Subject: [PATCH] =?UTF-8?q?expo-ble=E6=A8=A1=E5=9D=97=E6=B5=8B=E8=AF=95dem?= =?UTF-8?q?o=20=E8=81=94=E8=B0=83BLE=E6=A8=A1=E5=9D=97=20take=203=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=B7=9F=E5=A4=9Alog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ble/protocol/Constants.ts | 2 ++ ble/protocol/ProtocolManager.ts | 32 ++++++++++++++++------------- ble/services/BleProtocolService.ts | 4 ++-- ble/services/FileTransferService.ts | 2 +- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/ble/protocol/Constants.ts b/ble/protocol/Constants.ts index 9d87b1c..95b07ca 100644 --- a/ble/protocol/Constants.ts +++ b/ble/protocol/Constants.ts @@ -17,6 +17,7 @@ export const FRAME_CONSTANTS = { FOOTER_SIZE: 1, FRAME_INTERVAL: 35, } as const; +export type FRAME_HEAD = typeof FRAME_CONSTANTS.HEAD_DEVICE_TO_APP | typeof FRAME_CONSTANTS.HEAD_APP_TO_DEVICE; export const COMMAND_TYPES = { ACTIVATION_QUERY: 0x01, @@ -30,6 +31,7 @@ export const COMMAND_TYPES = { DEVICE_INFO_SETTINGS: 0x0d, DEVICE_IDENTITY_CHECK: 0x0e, } as const; +export type COMMAND_TYPES = typeof COMMAND_TYPES[keyof typeof COMMAND_TYPES]; export const RESPONSE_TYPES = { ACTIVATION_STATUS: 0x01, diff --git a/ble/protocol/ProtocolManager.ts b/ble/protocol/ProtocolManager.ts index 3874a37..47f3535 100644 --- a/ble/protocol/ProtocolManager.ts +++ b/ble/protocol/ProtocolManager.ts @@ -1,5 +1,5 @@ import {ProtocolFrame} from './types'; -import {FRAME_CONSTANTS} from './Constants'; +import {FRAME_CONSTANTS, COMMAND_TYPES, FRAME_HEAD} from './Constants'; export class ProtocolManager { @@ -15,22 +15,26 @@ export class ProtocolManager { } const checksumV1 = (0 - sum) & 0xff const checksum = (~sum + 1) & 0xff - console.debug(`[ProtocolManager] Checksum V1 calculated: 0 - ${sum} = ${checksumV1}`); - console.debug(`[ProtocolManager] Checksum calculated: 0 - ${sum} = ${checksum}`); + console.debug(`[ProtocolManager] Checksum V1 calculated: 0 - ${sum} = ${checksumV1.toString(16).padStart(2, '0')}`); + console.debug(`[ProtocolManager] Checksum calculated: 0 - ${sum} = ${checksum.toString(16).padStart(2, '0')}`); return checksum; } static verifyChecksum(frameData: Uint8Array, expectedChecksum: number): boolean { - const calculated = this.calculateChecksum(frameData); - return calculated === expectedChecksum; + return (frameData.length + expectedChecksum) === 0; } static createFrame( - type: number, + type: COMMAND_TYPES, data: Uint8Array, - head: number = FRAME_CONSTANTS.HEAD_APP_TO_DEVICE, + head: FRAME_HEAD = FRAME_CONSTANTS.HEAD_APP_TO_DEVICE, requireFragmentation: boolean = true ): Uint8Array[] { + // Max pages index is 4 bytes, so we can fit up to 65535 pages of data + const maxDataSize = 0xffff * (FRAME_CONSTANTS.HEADER_SIZE + FRAME_CONSTANTS.MAX_DATA_SIZE + FRAME_CONSTANTS.FOOTER_SIZE); + if (data.length > maxDataSize) { + throw new Error(`Data size ${data.length} exceeds max size ${maxDataSize}`); + } if (data.length <= FRAME_CONSTANTS.MAX_DATA_SIZE || !requireFragmentation) { console.debug(`[ProtocolManager] Frame size ${data.length} is less than max size ${FRAME_CONSTANTS.MAX_DATA_SIZE}, not fragmented`); return [this.createSingleFrame(type, data, head)]; @@ -40,7 +44,7 @@ export class ProtocolManager { } } - private static createSingleFrame(type: number, data: Uint8Array, head: number): Uint8Array { + private static createSingleFrame(type: COMMAND_TYPES, data: Uint8Array, head: FRAME_HEAD): Uint8Array { const buffer = new Uint8Array(FRAME_CONSTANTS.HEADER_SIZE + data.length + FRAME_CONSTANTS.FOOTER_SIZE); let offset = 0; @@ -71,7 +75,7 @@ export class ProtocolManager { return buffer; } - private static createFragmentedFrames(type: number, data: Uint8Array, head: number): Uint8Array[] { + private static createFragmentedFrames(type: COMMAND_TYPES, data: Uint8Array, head: FRAME_HEAD): Uint8Array[] { const frames: Uint8Array[] = []; const totalSize = data.length; const maxDataSize = FRAME_CONSTANTS.MAX_DATA_SIZE; @@ -92,9 +96,7 @@ export class ProtocolManager { buffer[offset++] = (totalPages >> 8) & 0xff; buffer[offset++] = totalPages & 0xff; - // curPage (descending order usually? No, BleManagerV2 comments said: "Protocol specifies: page numbers count down from highest to 0") - // Wait, ProtocolUtilsV2 code: - // const curPageVal = totalPages - 1 - i; + // Protocol specifies: page numbers count down from highest to 0 const curPageVal = totalPages - 1 - i; buffer[offset++] = (curPageVal >> 8) & 0xff; @@ -103,13 +105,15 @@ export class ProtocolManager { // dataLen buffer[offset++] = (chunk.length >> 8) & 0xff; buffer[offset++] = chunk.length & 0xff; - console.debug(`chunk length = ${chunk.length}, buffer 8 header = ${buffer.slice(0, 8).toString()}`) + const hexHeader = Array.from(buffer.slice(0, offset)).map(b => b.toString(16).padStart(2, '0')).join(' '); + console.debug(`chunk length = ${chunk.length}, buffer 8 header = ${hexHeader}`) // data buffer.set(chunk, offset); offset += chunk.length; buffer[offset] = this.calculateChecksum(buffer.slice(0, offset)); - + const verify = this.verifyChecksum(buffer.slice(0, offset), buffer[offset]); + console.debug(`[ProtocolManager] Verify checksum: ${verify}`); frames.push(buffer); } diff --git a/ble/services/BleProtocolService.ts b/ble/services/BleProtocolService.ts index 440f49a..e3957b7 100644 --- a/ble/services/BleProtocolService.ts +++ b/ble/services/BleProtocolService.ts @@ -1,6 +1,6 @@ import {BleClient} from '../core/BleClient'; import {ProtocolManager} from '../protocol/ProtocolManager'; -import {BLE_UUIDS, FRAME_CONSTANTS} from '../protocol/Constants'; +import {BLE_UUIDS, COMMAND_TYPES, FRAME_CONSTANTS} from '../protocol/Constants'; import {ProtocolFrame} from '../protocol/types'; import {Buffer} from 'buffer'; import {Subscription} from 'react-native-ble-plx'; @@ -139,7 +139,7 @@ export class BleProtocolService { } } - public async send(deviceId: string, type: number, data: object | ArrayBuffer, onProgress?: (progress: number) => void): Promise { + public async send(deviceId: string, type: COMMAND_TYPES, data: object | ArrayBuffer, onProgress?: (progress: number) => void): Promise { let payload: Uint8Array; if (data instanceof ArrayBuffer) { payload = new Uint8Array(data); diff --git a/ble/services/FileTransferService.ts b/ble/services/FileTransferService.ts index c84fa71..9790112 100644 --- a/ble/services/FileTransferService.ts +++ b/ble/services/FileTransferService.ts @@ -15,7 +15,7 @@ export class FileTransferService { return FileTransferService.instance; } - public async transferFile(deviceId: string, filePath: string, type: number, onProgress?: (progress: number) => void): Promise { + public async transferFile(deviceId: string, filePath: string, type: COMMAND_TYPES, onProgress?: (progress: number) => void): Promise { try { const response = await fetch(filePath); if (!response.ok) {