On this page
Low-level transport plugin
Use this only when WebUSB / React Native BLE / official native adapters cannot meet your scenario. You will own native BLE lifecycle and ordered I/O, so expect higher maintenance cost.
Note: The
lowlevelenv andLowlevelTransportSharedPluginare designed for BLE (Bluetooth) communication only. For USB communication in Node.js/CLI environments, useNodeUsbTransport(envnode-usb) instead — it handles protocol encoding and USB I/O directly via libusb without needing a plugin.
Our Bluetooth SDK is developed using React Native. You may not be using React Native to develop your application, for example, you may be using Swift, Kotlin, or Flutter. At this point, adding a dependency on React Native to your project may not be a good choice.
We provide a new communication method where you can use the LowlevelTransportSharedPlugin to communicate with the hardware. You are free to use any technology stack of your choice. You will need to finish the protocol for device connect, disconnect, send data, and receive data.
Quick Reference
OneKey BLE UUIDs
| Purpose | UUID |
|---|---|
| Service | 00000001-0000-1000-8000-00805f9b34fb |
| Write Characteristic | 00000002-0000-1000-8000-00805f9b34fb |
| Notify Characteristic | 00000003-0000-1000-8000-00805f9b34fb |
Protocol V1 BLE Header Data Example
The following header is specific to Protocol V1; do not use it to parse Protocol V2 notifications.
Raw data: 3F 23 23 00 02 00 00 00 0A 12 08 ...
│ │ │ │ │ │ │ └─ Protobuf payload starts
│ │ │ │ │ └────────┴─ Payload length: 0x0000000A = 10 bytes
│ │ │ └──┴─ Message type: 0x0002
└──┴──┴─ Magic: 0x3F 0x23 0x23 (fixed identifier)Detect header packet: First 3 bytes are 3F 23 23
Native examples (all based on the Low-Level Transport Plugin):
- Android Native — WebView + JSBridge + Nordic BLE
- iOS Native — WebView + JSBridge + CoreBluetooth
- Flutter Native — WebView + platform channel + BLE
LowlevelTransportSharedPlugin Interface
import type { OneKeyDeviceInfoBase } from '@onekeyfe/hd-transport';
export type LowLevelDevice = OneKeyDeviceInfoBase & { id: string; name: string };
export type LowlevelTransportSharedPlugin = {
enumerate: () => Promise<LowLevelDevice[]>;
send: (uuid: string, data: string, options?: { withoutResponse?: boolean }) => Promise<void>;
receive: (uuid?: string) => Promise<string>;
connect: (uuid: string) => Promise<void>;
disconnect: (uuid: string) => Promise<void>;
getProtocolV2PacketCapacity?: (uuid: string) => number | undefined | Promise<number | undefined>;
init: () => Promise<void>;
version: string;
};WebUSB vs BLE: Protocol V1 framing
These are Protocol V1 transport details. The low-level plugin is a BLE adapter; the SDK owns protocol detection, encoding and reassembly.
| WebUSB (Desktop) | BLE (Native Mobile) | |
|---|---|---|
| Packet size | Fixed 64 bytes | Variable (MTU-dependent) |
| Packet prefix | 0x3F on every packet | 0x3F 0x23 0x23 only on header |
| Padding | Zero-pad to 64 bytes | No padding needed |
receive() returns | One 64-byte frame at a time | Raw BLE notification chunk (hex) |
WebUSB Protocol V1 (Desktop)
Transport uses fixed 64-byte frames; the SDK handles message assembly.
Magic bytes:
0x3F 0x23 0x23are fixed protocol identifier bytes used to detect packet boundaries.
First packet:
| Offset | Length | Content |
|---|---|---|
| 0 | 3 | Magic: 0x3F 0x23 0x23 |
| 3 | 2 | Message type (BE uint16) |
| 5 | 4 | Payload length (BE uint32) |
| 9 | 55 | First 55 bytes of protobuf (zero-padded) |
Subsequent packets:
| Offset | Length | Content |
|---|---|---|
| 0 | 1 | Magic: 0x3F |
| 1 | 63 | Remaining protobuf bytes (zero-padded) |
BLE Protocol (Native Mobile)
Return one queued, unmodified BLE notification chunk as hex from each receive(uuid) call. Keep notification order and preserve chunks received before a reader is waiting. Do not strip protocol headers, pad to USB’s 64-byte packets, or decode protobuf in the plugin.
- Protocol V1: the SDK recognizes the
3F2323header, reads the payload length and collects continuation chunks. It also accepts the legacy preassembledtype + length + payloadmessage for V1 compatibility. - Protocol V2: the SDK assembles raw chunks into frames and handles CRC, sequence and message reassembly. A V1-only header detector or assembler must not process this data.
send(uuid, data, options)receives hex prepared by the SDK. Write bytes in order, propagate I/O failures and honorwithoutResponsewhen supplied.- Report the actual maximum bytes accepted by one BLE characteristic write through
getProtocolV2PacketCapacity. The SDK uses a 192-byte fallback and caps V2 packets at 244 bytes. Return the write capacity, not the ATT MTU including its overhead. - On disconnect, reject pending reads and discard queued notifications from the old connection. Do not deliver them after reconnecting.
Initialization Example
This adapter fragment delegates to a native bridge that must implement the interface above. Its methods must resolve only after the corresponding operation completes, or reject on failure.
import HardwareSDK from '@onekeyfe/hd-common-connect-sdk';
import type { LowlevelTransportSharedPlugin } from '@onekeyfe/hd-transport';
// Implement these methods in your native BLE bridge before initialization.
declare const nativeBle: LowlevelTransportSharedPlugin;
const plugin: LowlevelTransportSharedPlugin = {
enumerate: () => nativeBle.enumerate(),
send: (uuid, data, options) => nativeBle.send(uuid, data, options),
receive: uuid => nativeBle.receive(uuid),
connect: uuid => nativeBle.connect(uuid),
disconnect: uuid => nativeBle.disconnect(uuid),
getProtocolV2PacketCapacity: uuid => nativeBle.getProtocolV2PacketCapacity?.(uuid),
init: () => nativeBle.init(),
version: nativeBle.version,
};
const result = await HardwareSDK.init({ env: 'lowlevel', debug: false }, undefined, plugin);
if (!result) throw new Error('SDK initialization failed');Next Steps
- Android Guide — Bonding, permissions and connection completion
- iOS Guide — CoreBluetooth implementation
- Flutter Guide — Platform channel bridge