Skip to Content
HardwareConceptsLow-Level Transport

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 lowlevel env and LowlevelTransportSharedPlugin are designed for BLE (Bluetooth) communication only. For USB communication in Node.js/CLI environments, use NodeUsbTransport (env node-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

PurposeUUID
Service00000001-0000-1000-8000-00805f9b34fb
Write Characteristic00000002-0000-1000-8000-00805f9b34fb
Notify Characteristic00000003-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):

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 sizeFixed 64 bytesVariable (MTU-dependent)
Packet prefix0x3F on every packet0x3F 0x23 0x23 only on header
PaddingZero-pad to 64 bytesNo padding needed
receive() returnsOne 64-byte frame at a timeRaw BLE notification chunk (hex)

WebUSB Protocol V1 (Desktop)

Transport uses fixed 64-byte frames; the SDK handles message assembly.

Magic bytes: 0x3F 0x23 0x23 are fixed protocol identifier bytes used to detect packet boundaries.

First packet:

OffsetLengthContent
03Magic: 0x3F 0x23 0x23
32Message type (BE uint16)
54Payload length (BE uint32)
955First 55 bytes of protobuf (zero-padded)

Subsequent packets:

OffsetLengthContent
01Magic: 0x3F
163Remaining 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 3F2323 header, reads the payload length and collects continuation chunks. It also accepts the legacy preassembled type + length + payload message 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 honor withoutResponse when 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

Last updated on