From 83cdf722aca4ea4dcaee3f9e9ee10aa83bf5f24c Mon Sep 17 00:00:00 2001
From: Yudi Xiao <463708580@qq.com>
Date: Thu, 11 Dec 2025 16:42:41 +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=2010=20?=
=?UTF-8?q?=E6=96=B0=E5=A2=9Elog=20=E6=89=93=E5=8D=B0ani=E6=96=87=E4=BB=B6?=
=?UTF-8?q?=E5=89=8D=E5=90=8E512=E5=AD=97=E8=8A=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app/(tabs)/explore.tsx | 4 ++--
ble/core/BleClient.ts | 2 +-
ble/hooks/useBleExplorer.ts | 12 +++++++-----
ble/protocol/Constants.ts | 8 ++++----
ble/protocol/ProtocolManager.ts | 13 +++++--------
ble/services/BleProtocolService.ts | 16 ++++++++++------
ble/services/DeviceInfoService.ts | 2 +-
ble/services/FileTransferService.ts | 4 ++--
8 files changed, 32 insertions(+), 29 deletions(-)
diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/explore.tsx
index 4eb8430..1ee2bc0 100644
--- a/app/(tabs)/explore.tsx
+++ b/app/(tabs)/explore.tsx
@@ -16,7 +16,6 @@ export default function TabTwoScreen() {
version,
isActivated,
transferProgress,
- isTransferring,
discoveredDevices,
loading,
error,
@@ -118,7 +117,8 @@ export default function TabTwoScreen() {
{/* Transfer Status */}
File Transfer
- Transferring: {isTransferring ? 'Yes' : 'No'}
+ Converting: {loading.converting ? 'Yes' : 'No'}
+ Transferring: {loading.transferring ? 'Yes' : 'No'}
Progress: {transferProgress}%
diff --git a/ble/core/BleClient.ts b/ble/core/BleClient.ts
index 4d76ea7..fed144e 100644
--- a/ble/core/BleClient.ts
+++ b/ble/core/BleClient.ts
@@ -96,7 +96,7 @@ export class BleClient {
requestMTU: BLE_UUIDS.REQUEST_MTU
});
if (device.mtu < BLE_UUIDS.REQUEST_MTU) {
- console.log("MTU not supported, requesting default to ", BLE_UUIDS.REQUEST_MTU);
+ console.log(`MTU ${device.mtu} not supported, requesting default to ${BLE_UUIDS.REQUEST_MTU}`);
device = await device.requestMTU(BLE_UUIDS.REQUEST_MTU);
// Give some time for the stack to stabilize after MTU change
await new Promise(resolve => setTimeout(resolve, 500));
diff --git a/ble/hooks/useBleExplorer.ts b/ble/hooks/useBleExplorer.ts
index df9e2fb..4869027 100644
--- a/ble/hooks/useBleExplorer.ts
+++ b/ble/hooks/useBleExplorer.ts
@@ -6,9 +6,9 @@ import {
BleClient,
BleDevice,
BleError,
- BleProtocolService, COMMAND_TYPES,
+ BleProtocolService, APP_COMMAND_TYPES,
ConnectionState,
- DeviceInfo,
+ DeviceInfo, COMMAND_TYPES,
} from '../index';
import {DeviceInfoService} from '../services/DeviceInfoService'
import {FileTransferService} from "../services/FileTransferService";
@@ -25,11 +25,11 @@ interface BleState {
version: string;
isActivated: boolean;
transferProgress: number;
- isTransferring: boolean;
discoveredDevices: BleDevice[];
loading: {
connecting: boolean;
querying: boolean;
+ converting: boolean;
transferring: boolean;
};
error: string | null;
@@ -50,11 +50,11 @@ export const useBleExplorer = () => {
version: '',
isActivated: false,
transferProgress: 0,
- isTransferring: false,
discoveredDevices: [],
loading: {
connecting: false,
querying: false,
+ converting: false,
transferring: false,
},
error: null,
@@ -334,7 +334,7 @@ export const useBleExplorer = () => {
};
const convertToANI = useCallback(async (tempDir: Directory, media: ImagePicker.ImagePickerAsset): Promise => {
- const tempFile = new File(tempDir, `${media.fileName}.ani`)
+ const tempFile = new File(tempDir, `${media.fileName?.split('.')[0]}.ani`)
const formData = new FormData();
formData.append('file', {
uri: media.uri,
@@ -370,7 +370,9 @@ export const useBleExplorer = () => {
if (media.type === 'video') {
let tempFile: File;
console.debug(`Converting video: ${media.fileName || 'video'}...`);
+ setState(prev => ({...prev, loading: {...prev.loading, converting: true}}));
tempFile = await convertToANI(tempDir, media)
+ setState(prev => ({...prev, loading: {...prev.loading, converting: false}}));
console.log(`Transferring converted file to device...`);
await fileTransferService.transferFile(
state.connectedDevice.id,
diff --git a/ble/protocol/Constants.ts b/ble/protocol/Constants.ts
index 6abe9b5..d6b75ad 100644
--- a/ble/protocol/Constants.ts
+++ b/ble/protocol/Constants.ts
@@ -2,7 +2,6 @@ export const PROTOCOL_VERSION = "1.0.0";
export const BLE_UUIDS = {
SERVICE: '000002c4-0000-1000-8000-00805f9b34fb',
- SERVICE_SHORT: 'ae00',
BROADCAST_CHARACTERISTIC: "000002c1-0000-1000-8000-00805f9b34fb",
WRITE_CHARACTERISTIC: '000002c5-0000-1000-8000-00805f9b34fb',
READ_CHARACTERISTIC: '000002c6-0000-1000-8000-00805f9b34fb',
@@ -11,13 +10,13 @@ export const BLE_UUIDS = {
export const FRAME_CONSTANTS = {
HEAD_DEVICE_TO_APP: 0xb0,
- // HEAD_APP_TO_DEVICE: 0xc7,
HEAD_APP_TO_DEVICE: 0xb1,
MAX_DATA_SIZE: 496,
HEADER_SIZE: 8,
FOOTER_SIZE: 1,
- FRAME_INTERVAL: 35,
+ FRAME_INTERVAL: 35, // package transfer idle interval in ms, set 35 ms for ble device have enough time to process data
} as const;
+
export type FRAME_HEAD = typeof FRAME_CONSTANTS.HEAD_DEVICE_TO_APP | typeof FRAME_CONSTANTS.HEAD_APP_TO_DEVICE;
export const COMMAND_TYPES = {
@@ -32,7 +31,8 @@ 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 type APP_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 16451a1..11caa1e 100644
--- a/ble/protocol/ProtocolManager.ts
+++ b/ble/protocol/ProtocolManager.ts
@@ -1,5 +1,5 @@
import {ProtocolFrame} from './types';
-import {FRAME_CONSTANTS, COMMAND_TYPES, FRAME_HEAD} from './Constants';
+import {FRAME_CONSTANTS, APP_COMMAND_TYPES, FRAME_HEAD} from './Constants';
export class ProtocolManager {
@@ -13,9 +13,7 @@ export class ProtocolManager {
for (let i = 0; i < frameData.length; i++) {
sum += frameData[i];
}
- const checksumV1 = (0 - sum) & 0xff
const checksum = (~sum + 1) & 0xff
- 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;
}
@@ -25,10 +23,9 @@ export class ProtocolManager {
}
static createFrame(
- type: COMMAND_TYPES,
+ type: APP_COMMAND_TYPES,
data: Uint8Array,
head: FRAME_HEAD = FRAME_CONSTANTS.HEAD_APP_TO_DEVICE,
- requireFragmentation: boolean = true,
maxDataSize: number = FRAME_CONSTANTS.MAX_DATA_SIZE
): Uint8Array[] {
// Max pages index is 4 bytes, so we can fit up to 65535 pages of data
@@ -36,7 +33,7 @@ export class ProtocolManager {
if (data.length > maxTotalSize) {
throw new Error(`Data size ${data.length} exceeds max size ${maxTotalSize}`);
}
- if (data.length <= maxDataSize || !requireFragmentation) {
+ if (data.length <= maxDataSize) {
console.debug(`[ProtocolManager] Frame size ${data.length} is less than max size ${maxDataSize}, not fragmented`);
return [this.createSingleFrame(type, data, head)];
} else {
@@ -45,7 +42,7 @@ export class ProtocolManager {
}
}
- private static createSingleFrame(type: COMMAND_TYPES, data: Uint8Array, head: FRAME_HEAD): Uint8Array {
+ private static createSingleFrame(type: APP_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;
@@ -78,7 +75,7 @@ export class ProtocolManager {
return buffer;
}
- private static createFragmentedFrames(type: COMMAND_TYPES, data: Uint8Array, head: FRAME_HEAD, maxDataSize: number): Uint8Array[] {
+ private static createFragmentedFrames(type: APP_COMMAND_TYPES, data: Uint8Array, head: FRAME_HEAD, maxDataSize: number): Uint8Array[] {
const frames: Uint8Array[] = [];
const totalSize = data.length;
const totalPages = Math.ceil(totalSize / maxDataSize);
diff --git a/ble/services/BleProtocolService.ts b/ble/services/BleProtocolService.ts
index 1fb2014..2051353 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, COMMAND_TYPES, FRAME_CONSTANTS} from '../protocol/Constants';
+import {BLE_UUIDS, APP_COMMAND_TYPES, FRAME_CONSTANTS} from '../protocol/Constants';
import {ProtocolFrame} from '../protocol/types';
import {Buffer} from 'buffer';
import {Subscription} from 'react-native-ble-plx';
@@ -139,13 +139,16 @@ export class BleProtocolService {
}
}
- public async send(deviceId: string, type: COMMAND_TYPES, data: object | ArrayBuffer | Uint8Array, onProgress?: (progress: number) => void): Promise {
+ public async send(deviceId: string, type: APP_COMMAND_TYPES, data: object | ArrayBuffer | Uint8Array, onProgress?: (progress: number) => void): Promise {
let payload: Uint8Array;
if (data instanceof ArrayBuffer) {
+ console.debug("[BleProtocolService] Sending ArrayBuffer");
payload = new Uint8Array(data);
} else if (data instanceof Uint8Array) {
+ console.debug("[BleProtocolService] Sending Uint8Array");
payload = data;
} else {
+ console.debug("[BleProtocolService] Sending JSON payload");
const jsonStr = JSON.stringify(data);
payload = new Uint8Array(Buffer.from(jsonStr));
}
@@ -158,8 +161,10 @@ export class BleProtocolService {
const safeMaxDataSize = Math.max(1, Math.min(maxPayloadSize, FRAME_CONSTANTS.MAX_DATA_SIZE));
console.debug(`[BleProtocolService] Sending with MTU=${mtu}, maxDataSize=${safeMaxDataSize}`);
-
- const frames = ProtocolManager.createFrame(type, payload, FRAME_CONSTANTS.HEAD_APP_TO_DEVICE, true, safeMaxDataSize);
+ const rawPayloadHex = payload.reduce((acc, val) => acc + val.toString(16).padStart(2, '0') + ' ', '');
+ const formattedRawPayloadHex = rawPayloadHex.substring(0, 512) + "\n......\n" + rawPayloadHex.substring(rawPayloadHex.length - 512);
+ console.debug(`[BleProtocolService] Sending payload size=${payload.byteLength}, raw payload hex=\n${formattedRawPayloadHex}`);
+ const frames = ProtocolManager.createFrame(type, payload, FRAME_CONSTANTS.HEAD_APP_TO_DEVICE, safeMaxDataSize);
const total = frames.length;
console.debug(`Sending ${total} frames`);
for (let i = 0; i < total; i++) {
@@ -171,8 +176,7 @@ export class BleProtocolService {
if (onProgress) {
onProgress((i + 1) / total);
}
-
- console.debug("Wrote frame", result);
+ // console.debug("Wrote frame", result);
}
}
}
diff --git a/ble/services/DeviceInfoService.ts b/ble/services/DeviceInfoService.ts
index 3423059..9c29654 100644
--- a/ble/services/DeviceInfoService.ts
+++ b/ble/services/DeviceInfoService.ts
@@ -1,5 +1,5 @@
import {BleProtocolService} from './BleProtocolService';
-import {COMMAND_TYPES, RESPONSE_TYPES} from '../protocol/Constants';
+import {APP_COMMAND_TYPES, COMMAND_TYPES, RESPONSE_TYPES} from '../protocol/Constants';
import {DeviceInfo, ActivationStatus} from '../protocol/types';
import {Buffer} from 'buffer';
diff --git a/ble/services/FileTransferService.ts b/ble/services/FileTransferService.ts
index c0568c0..783b629 100644
--- a/ble/services/FileTransferService.ts
+++ b/ble/services/FileTransferService.ts
@@ -1,5 +1,5 @@
import {BleProtocolService} from './BleProtocolService';
-import {COMMAND_TYPES} from '../protocol/Constants';
+import {APP_COMMAND_TYPES, COMMAND_TYPES} from '../protocol/Constants';
export class FileTransferService {
@@ -16,7 +16,7 @@ export class FileTransferService {
return FileTransferService.instance;
}
- public async transferFile(deviceId: string, filePath: string, type: COMMAND_TYPES, onProgress?: (progress: number) => void): Promise {
+ public async transferFile(deviceId: string, filePath: string, type: APP_COMMAND_TYPES, onProgress?: (progress: number) => void): Promise {
try {
const response = await fetch(filePath);
if (!response.ok) {