更新记录

1.0.1(2026-04-29)

  • 适配uniappx项目
  • 新增uniappx示例代码

1.0.0(2026-04-29)

  • 新版发布

平台兼容性

uni-app(4.87)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
× ×
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 小红书小程序 快应用-华为 快应用-联盟
× × × × × × × × × × × ×

uni-app x(4.87)

Chrome Safari Android iOS 鸿蒙 微信小程序
× × ×

特别提醒

  • 购买本插件前,请先试用,请先试用,请先试用,确认满足需求之后再行购买。虚拟物品一旦购买之后无法退款。
  • 如有使用上的疑问、bug,可以进交流群联系作者;
  • 请在合法范围内使用,若使用本插件做非法开发,本方概不负责;
  • iOS使用示例demo测试时,要先修改manifest中项目名称后再打包(名称长度尽量短一点),不然会因为名称过长,导致App启动不了。
  • uniapp-x项目调用api需要加上 as 类型转换

yt-ble(跨端 UTS BLE 插件)

yt-ble 是一个基于 UTS 的低功耗蓝牙插件,支持 App 端:

  • Android
  • iOS
  • HarmonyOS

支持能力:

  • 蓝牙初始化
  • 扫描设备(名称/服务过滤)
  • 连接与断开
  • 获取服务和特征
  • 读特征
  • 订阅通知(notify)
  • 写特征(字节/字符串、UTF-8/GBK/HEX)
  • 获取/设置 MTU(iOS/Harmony 根据平台能力做兼容处理)

1. 安装与引入

导入插件后,确认项目中存在目录:uni_modules/yt-ble

在页面或业务模块中引入:

import * as BleManager from '@/uni_modules/yt-ble'
// 或相对路径:import * as BleManager from '../../uni_modules/yt-ble'

2. 推荐调用流程

  1. initBLE()
  2. startScan(...)
  3. 选择目标设备后 connectDevice(...)
  4. getDeviceServices(...)
  5. 根据服务/特征调用 notify(...) / read(...) / writeData(...)
  6. 结束时 disconnect(),必要时 stopScan()

3. API 说明

initBLE(): void

初始化 BLE 管理器。建议在页面初始化时调用一次。

startScan(options: ScanPara): void

开始扫描。

ScanPara

  • scantime: number 扫描时长(毫秒)
  • showEmptyName: boolean 是否返回空名称设备
  • btNameFilter?: string 名称包含过滤
  • fliterNames?: string[] 名称白名单(包含任一关键字即通过)
  • serviceUuids?: string[] 服务 UUID 过滤
  • onScanResult(res: ApiResult) 扫描数据/错误回调
  • scanComplate?() 扫描结束回调

stopScan(): void

停止扫描。

connectDevice(connectPara: ConnectPara): void

连接设备。

ConnectPara

  • deviceId: string
  • onConnectResult(res: ConnectResult)

ConnectResult.type 约定:

  • "0" 连接成功
  • "2" 连接断开
  • 其它值:错误码

disconnect(): void

断开当前连接设备。

getDeviceServices(servicesPara: ServicesPara): void

获取设备服务与特征。

ServicesResult.dataBleServices[],其中每个特征包含属性:

  • read
  • write
  • writeNoResponse
  • notify

read(readDataPara: ReadDataPara): void

读取特征值。

成功回调 onReadDataResult

  • data?: number[]
  • hexValue?: string
  • utf8Value?: string
  • gbkValue?: string

notify(notifiDataPara: NotifiDataPara): void

订阅/取消订阅通知。

  • isNotifyEnabled: true 开启
  • isNotifyEnabled: false 关闭

通知数据通过 onNotifyDataResult 返回,数据结构同 read

writeData(writeDataPara: WriteDataPara): void

写入特征值。

支持两种写法:

1) 字节数组写入:data: number[] 2) 文本写入:text: string + encoding

encoding

  • 0:UTF-8
  • 1:GBK/GB18030
  • 2:HEX(支持空格、换行、0x 前缀)

writeType

  • 0:默认写入(有响应)
  • 1:无响应写入

setMtu(setMtuPara: SETMtuPara): void

设置 MTU(不同平台能力有差异,iOS/Harmony 按平台能力兼容返回)。

getMtu(getMtuPara: GETMtuPara): void

获取当前 MTU(各平台按实现返回当前值或兼容值)。


4. 主要类型(节选)

utssdk/interface.uts 为准,核心类型如下:

  • ApiResult{ type, msg?, data? }
  • BleDevice{ deviceId, name?, localName?, RSSI? ... }
  • BleServices{ uuid, characteristics? }
  • ReadNotifyData{ data?, hexValue?, utf8Value?, gbkValue? }

5. 常见错误码

不同平台可能略有差异,常见值如下:

  • 10000 参数错误 / deviceId 非法 / 连接异常
  • 10001 已断开
  • 10002 蓝牙不可用或扫描失败
  • 10006 未连接
  • 10008 写入目标特征不存在或写失败
  • 10009 读取目标特征不存在或读失败
  • 10010 写入中 / MTU 不支持(平台差异)
  • 10012 HEX 字符串非法

6. 使用建议

  • 扫描前先 initBLE(),并确保系统蓝牙已开启。
  • 连接成功后先 getDeviceServices(),再进行 read/write/notify
  • 写入二进制协议建议优先使用 text + encoding:2(HEX)或直接 data:number[]
  • iOS 的 deviceId 是 UUID 字符串;Android 常见为 MAC 字符串。
  • 退出页面时建议调用 stopScan()disconnect() 做资源释放。

7. uniapp示例

<template>
    <view class="content">
        <view class="item">
            <view style="display: flex;flex-direction: row;align-items: center;margin-top: 15rpx;">
                <text class="read_title">当前连接设备:</text>
                <text class="read_title">{{currentDeviceName}}</text>
            </view>
            <view style="display: flex;flex-direction: row;width: 750rpx;margin-top: 35rpx;align-items: center;">
                <view class="brn" @click="startScan">开始扫描</view>
                <view class="brn" @click="disconnect">断开连接</view>
                <view class="brn" @click="stopScan">停止扫描</view>
                <view class="brn" @click="getServices">获取蓝牙服务</view>
            </view>
        </view>
        <view class="item_data">
            <text style="font-size: 32rpx;color: black;">数据读写</text>
            <view style="display: flex;flex-direction: row;align-items: center;margin-top: 15rpx;">
                <text class="read_title">读取到的数据:</text>
                <text class="read_title">{{readData}}</text>
            </view>
            <view style="display: flex;flex-direction: row;width: 750rpx;margin-top: 15rpx;align-items: center;">
                <view class="brn" style="width: 150rpx;" @click="readAction"><text>读取数据</text></view>
                <view class="brn" style="width: 150rpx;" @click="writeData"><text>写入数据</text></view>
                <view class="brn" style="width: 150rpx;" @click="getMTU"><text>获取MTU</text></view>
                <view class="brn" style="width: 150rpx;" @click="configMTU"><text>设置MTU</text></view>
            </view>
            <view style="display: flex;flex-direction: row;width: 750rpx;margin-top: 35rpx;align-items: center;">
                <view class="brn" style="width: 180rpx;" @click="notify"><text>监听notify</text></view>
            </view>
            <view style="display: flex;flex-direction: row;align-items: center;margin-top: 15rpx;margin-bottom: 20rpx;">
                <text class="read_title">notify数据:</text>
                <text class="read_title">{{notifyData}}</text>
            </view>
        </view>
        <scroll-view
            style="width: 750rpx;height: 700rpx;background-color: antiquewhite;margin-top: 20rpx;padding: 0 20rpx;box-sizing: border-box;"
            scroll-y>
            <view class="device_view" v-for="(item,index) in devices" @click="connectDevice(item)">
                <view class="item_row">
                    <text class="title">名称:</text>
                    <text class="value">{{item.name}}</text>
                </view>
                <view class="item_row">
                    <text class="title">deviceId:</text>
                    <text class="value">{{item.deviceId}}</text>
                </view>
                <view class="item_row">
                    <text class="title">RSSI:</text>
                    <text class="value">{{item.RSSI}}</text>
                </view>
            </view>
        </scroll-view>
    </view>
</template>

<script>
    import * as BleManager from '../../uni_modules/yt-ble'
    export default {
        data() {
            return {
                devices: [],
                currentDeviceID: '',
                currentDeviceName: '无',
                readData: '无', //读取到的数据
                notifyData: '无', //notify数据
                isNotifyEnabled: false //notify状态
            }
        },
        onLoad() {
            BleManager.initBLE();
        },
        methods: {
            startScan() {
                this.devices = [];
                BleManager.startScan({
                    scantime: 10000,
                    showEmptyName: false,
                    onScanResult: (res) => {
                        if (res['type'] == "0") {
                            this.devices.push(res["data"]);
                        }
                    },
                    scanComplate: () => {
                        console.log('扫描完成');
                    }
                });
            },
            //停止扫描
            stopScan() {
                BleManager.stopScan();
            },
            //连接设备
            connectDevice(item) {
                let deviceId = item["deviceId"] + ""
                BleManager.connectDevice({
                    deviceId: deviceId,
                    onConnectResult: (res) => {
                        let type = res["type"];
                        if (type == "0") {
                            this.currentDeviceID = res.deviceId + '';
                            this.currentDeviceName = item.name + '';
                        }
                        console.log(res["msg"])
                    }
                });
            },
            //断开连接
            disconnect() {
                BleManager.disconnect(this.currentDeviceID);
            },
            //获取所有服务
            getServices() {
                console.log(this.currentDeviceID);
                BleManager.getDeviceServices({
                    deviceId: this.currentDeviceID,
                    onServicesResult: (res) => {
                        if (res.type == "0") {
                            console.log(res.data)
                        }
                        uni.showToast({
                            icon: 'none',
                            title: res.msg,
                            duration: 2000
                        })
                    }
                })
            },
            readAction() {
                //通过getDeviceServices获取
                let serviceId = '00001800-0000-1000-8000-00805F9B34FB';
                let characteristicId = '00002A00-0000-1000-8000-00805F9B34FB';
                // if (plus.os.name == 'iOS') {
                //  serviceId = "180A";
                //  characteristicId = "2A29";
                // }
                BleManager.read({
                    deviceId: this.currentDeviceID,
                    serviceId: serviceId,
                    characteristicId: characteristicId,
                    onReadDataResult: (res) => {
                        let value = res.data; //字节数组--原始数据
                        let gbkValue = res.gbkValue;
                        let utf8Value = res.utf8Value;
                        this.readData = res.utf8Value; //16进制HEX格式
                    },
                    onError: (err) => {
                        console.log(`Read数据出错:${err}`)
                    }
                })
            },
            //notify
            notify() {
                this.isNotifyEnabled = !this.isNotifyEnabled;
                //通过getDeviceServices获取
                let serviceId = '0000FFB0-0000-1000-8000-00805F9B34FB';
                let characteristicId = '0000FFB2-0000-1000-8000-00805F9B34FB';

                BleManager.notify({
                    deviceId: this.currentDeviceID,
                    serviceId: serviceId,
                    characteristicId: characteristicId,
                    isNotifyEnabled: this.isNotifyEnabled,
                    onNotifyDataResult: (res) => {
                        let value = res.data; //字节数组--原始数据
                        this.notifyData = res.hexValue; //16进制HEX格式
                    },
                    onNotifyStatu: (res) => {
                        console.log(res ? 'Notify监听打开' : 'Notify监听关闭')
                    }
                })
            },
            ///写入数据
            writeData() {
                //通过getDeviceServices获取
                let serviceId = '00001800-0000-1000-8000-00805F9B34FB';
                let characteristicId = '00002A00-0000-1000-8000-00805F9B34FB';
                // if (plus.os.name == 'iOS') {
                //  serviceId = "FFB0";
                //  characteristicId = "FFB1";
                // }
                BleManager.writeData({
                    deviceId: this.currentDeviceID,
                    serviceId: serviceId,
                    characteristicId: characteristicId,
                    text: 'Hello Digtal Scale',
                    encoding: 1,
                    onWriteOk: (deviceId) => {
                        console.log(`${deviceId}写入成功`)
                    },
                    onError: (err) => {
                        console.log(err);
                    }
                })
            },
            //获取mtu
            getMTU() {
                BleManager.getMtu({
                    deviceId: this.currentDeviceID,
                    onGetMtuResult: (res) => {
                        let mtu = res.mtu;
                        let deviceId = res.deviceId;
                        uni.showToast({
                            icon: 'none',
                            title: `当前设备MTU:${mtu}`
                        })
                    }
                })
            },
            //设置mtu
            configMTU() {
                BleManager.setMtu({
                    deviceId: this.currentDeviceID,
                    mtu: 25,
                    onSetMtuResult: (res) => {
                        let mtu = res.mtu;
                        let deviceId = res.deviceId;
                        let success = res.success;
                        uni.showToast({
                            icon: 'none',
                            title: success ? '设置成功' : '设置失败'
                        })
                    }
                })
            }

        }
    }
</script>

<style>
    .content {
        width: 750rpx;
        display: flex;
        flex-direction: column;
        height: 100%;
        background-color: whitesmoke;
    }

    .item {
        display: flex;
        flex-direction: column;
        width: 750rpx;
        padding: 20rpx;
        box-sizing: border-box;
        background-color: aquamarine;
    }

    .brn {
        background-color: cadetblue;
        height: 70rpx;
        padding: 0 10rpx;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        margin-left: 20rpx;
        border-radius: 8rpx;
    }

    .device_view {
        display: flex;
        width: 100%;
        padding: 10rpx 10rpx;
        box-sizing: border-box;
        display: flex;
        flex-direction: column;
        margin-bottom: 10rpx;
        background-color: aquamarine;
        border-radius: 8rpx;
    }

    .item_row {
        display: flex;
        flex-direction: row;
        align-items: center;
        padding: 0 20rpx;
        box-sizing: border-box;
    }

    .title {
        font-size: 32rpx;
        color: #333;
        font-weight: bold;
    }

    .value {
        font-size: 32rpx;
        color: brown;
        font-weight: bold;
    }

    .item_data {
        display: flex;
        width: 750rpx;
        background-color: burlywood;
        margin-top: 20rpx;
        padding: 10rpx 20rpx;
        box-sizing: border-box;
        flex-direction: column;
    }

    .read_title {
        font-size: 30rpx;
        color: aliceblue;
    }
</style>

8. Uniapp-x示例demo

<template>
    <view class="content">
        <view class="item">
            <view style="display: flex;flex-direction: row;align-items: center;margin-top: 15rpx;">
                <text class="read_title">当前连接设备:</text>
                <text class="read_title">{{currentDeviceName}}</text>
            </view>
            <view style="display: flex;flex-direction: row;width: 750rpx;margin-top: 35rpx;align-items: center;">
                <view class="brn" @click="startScan">开始扫描</view>
                <view class="brn" @click="disconnect">断开连接</view>
                <view class="brn" @click="stopScan">停止扫描</view>
                <view class="brn" @click="getServices">获取蓝牙服务</view>
            </view>
        </view>
        <view class="item_data">
            <text style="font-size: 32rpx;color: black;">数据读写</text>
            <view style="display: flex;flex-direction: row;align-items: center;margin-top: 15rpx;">
                <text class="read_title">读取到的数据:</text>
                <text class="read_title">{{readData}}</text>
            </view>
            <view style="display: flex;flex-direction: row;width: 750rpx;margin-top: 15rpx;align-items: center;">
                <view class="brn" style="width: 150rpx;" @click="readAction"><text>读取数据</text></view>
                <view class="brn" style="width: 150rpx;" @click="writeData"><text>写入数据</text></view>
                <view class="brn" style="width: 150rpx;" @click="getMTU"><text>获取MTU</text></view>
                <view class="brn" style="width: 150rpx;" @click="configMTU"><text>设置MTU</text></view>
            </view>
            <view style="display: flex;flex-direction: row;width: 750rpx;margin-top: 35rpx;align-items: center;">
                <view class="brn" style="width: 180rpx;" @click="notify"><text>监听notify</text></view>
            </view>
            <view style="display: flex;flex-direction: row;align-items: center;margin-top: 15rpx;margin-bottom: 20rpx;">
                <text class="read_title">notify数据:</text>
                <text class="read_title">{{notifyData}}</text>
            </view>
        </view>
        <scroll-view
            style="width: 750rpx;height: 700rpx;background-color: antiquewhite;margin-top: 20rpx;padding: 0 20rpx;box-sizing: border-box;"
            scroll-y>
            <view class="device_view" v-for="(item,index) in devices" @click="connectDevice(item)">
                <view class="item_row">
                    <text class="title">名称:</text>
                    <text class="value">{{item.name}}</text>
                </view>
                <view class="item_row">
                    <text class="title">deviceId:</text>
                    <text class="value">{{item.deviceId}}</text>
                </view>
                <view class="item_row">
                    <text class="title">RSSI:</text>
                    <text class="value">{{item.RSSI}}</text>
                </view>
            </view>
        </scroll-view>
    </view>
</template>

<script>
    import * as BleManager from '../../uni_modules/yt-ble'
    export default {
        data() {
            return {
                devices: [] as BleManager.BleDevice[],
                currentDeviceID: '',
                currentDeviceName: '无',
                readData: '无', //读取到的数据
                notifyData: '无', //notify数据
                isNotifyEnabled: false //notify状态
            }
        },
        onLoad() {
            BleManager.initBLE();
        },
        methods: {
            startScan() {
                this.devices = [];
                BleManager.startScan({
                    scantime: 10000,
                    showEmptyName: false,
                    onScanResult: (res) => {
                        let result = res as BleManager.ApiResult;
                        if (result.type == "0" && result.data != null) {
                            this.devices.push(result.data as BleManager.BleDevice);
                        }
                    },
                    scanComplate: () => {
                        console.log('扫描完成');
                    }
                } as BleManager.ScanPara);
            },
            //停止扫描
            stopScan() {
                BleManager.stopScan();
            },
            //连接设备
            connectDevice(item : BleManager.BleDevice) {
                let deviceId = item.deviceId
                BleManager.connectDevice({
                    deviceId: deviceId,
                    onConnectResult: (res : BleManager.ConnectResult) => {
                        let type = res.type;
                        if (type == "0") {
                            this.currentDeviceID = res.deviceId + '';
                            this.currentDeviceName = item.name + '';
                        }
                        console.log(res.msg)
                    }
                } as BleManager.ConnectPara);
            },
            //断开连接
            disconnect() {
                BleManager.disconnect(this.currentDeviceID);
            },
            //获取所有服务
            getServices() {
                BleManager.getDeviceServices({
                    deviceId: this.currentDeviceID,
                    onServicesResult: (res : BleManager.ServicesResult) => {
                        if (res.type == "0") {
                            console.log(res.data)
                        }
                        uni.showToast({
                            icon: 'none',
                            title: res.msg,
                            duration: 2000
                        })
                    }
                } as BleManager.ServicesPara)
            },
            readAction() {
                //通过getDeviceServices获取
                let serviceId = '00001800-0000-1000-8000-00805F9B34FB';
                let characteristicId = '00002A00-0000-1000-8000-00805F9B34FB';
                // if (plus.os.name == 'iOS') {
                //  serviceId = "180A";
                //  characteristicId = "2A29";
                // }
                BleManager.read({
                    deviceId: this.currentDeviceID,
                    serviceId: serviceId,
                    characteristicId: characteristicId,
                    onReadDataResult: (res : BleManager.ReadNotifyData) => {
                        let value = res.data; //字节数组--原始数据
                        let gbkValue = res.gbkValue;
                        let utf8Value = res.utf8Value;
                        this.readData = res.utf8Value + ''; //16进制HEX格式
                    },
                    onError: (err) => {
                        console.log(`Read数据出错:${err}`)
                    }
                } as BleManager.ReadDataPara)
            },
            //notify
            notify() {
                this.isNotifyEnabled = !this.isNotifyEnabled;
                //通过getDeviceServices获取
                let serviceId = '0000FFB0-0000-1000-8000-00805F9B34FB';
                let characteristicId = '0000FFB2-0000-1000-8000-00805F9B34FB';

                BleManager.notify({
                    deviceId: this.currentDeviceID,
                    serviceId: serviceId,
                    characteristicId: characteristicId,
                    isNotifyEnabled: this.isNotifyEnabled,
                    onNotifyDataResult: (res : BleManager.ReadNotifyData) => {
                        let value = res.data; //字节数组--原始数据
                        this.notifyData = res.hexValue + ''; //16进制HEX格式
                    },
                    onNotifyStatu: (res) => {
                        console.log(res ? 'Notify监听打开' : 'Notify监听关闭')
                    }
                } as BleManager.NotifiDataPara)
            },
            ///写入数据
            writeData() {
                //通过getDeviceServices获取
                let serviceId = '00001800-0000-1000-8000-00805F9B34FB';
                let characteristicId = '00002A00-0000-1000-8000-00805F9B34FB';
                // if (plus.os.name == 'iOS') {
                //  serviceId = "FFB0";
                //  characteristicId = "FFB1";
                // }
                BleManager.writeData({
                    deviceId: this.currentDeviceID,
                    serviceId: serviceId,
                    characteristicId: characteristicId,
                    text: 'Hello Digtal Scale',
                    encoding: 1,
                    onWriteOk: (deviceId) => {
                        console.log(`${deviceId}写入成功`)
                    },
                    onError: (err) => {
                        console.log(err);
                    }
                } as BleManager.WriteDataPara)
            },
            //获取mtu
            getMTU() {
                BleManager.getMtu({
                    deviceId: this.currentDeviceID,
                    onGetMtuResult: (res : BleManager.MtuResult) => {
                        let mtu = res.mtu;
                        let deviceId = res.deviceId;
                        uni.showToast({
                            icon: 'none',
                            title: `当前设备MTU:${mtu}`
                        })
                    }
                } as BleManager.GETMtuPara)
            },
            //设置mtu
            configMTU() {
                BleManager.setMtu({
                    deviceId: this.currentDeviceID,
                    mtu: 25,
                    onSetMtuResult: (res : BleManager.MtuResult) => {
                        let mtu = res.mtu;
                        let deviceId = res.deviceId;
                        let success = res.success;
                        uni.showToast({
                            icon: 'none',
                            title: success ? '设置成功' : '设置失败'
                        })
                    }
                } as BleManager.SETMtuPara)
            }

        }
    }
</script>

<style>
    .content {
        width: 750rpx;
        display: flex;
        flex-direction: column;
        height: 100%;
        background-color: whitesmoke;
    }

    .item {
        display: flex;
        flex-direction: column;
        width: 750rpx;
        padding: 20rpx;
        box-sizing: border-box;
        background-color: aquamarine;
    }

    .brn {
        background-color: cadetblue;
        height: 70rpx;
        padding: 0 10rpx;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        margin-left: 20rpx;
        border-radius: 8rpx;
    }

    .device_view {
        display: flex;
        width: 100%;
        padding: 10rpx 10rpx;
        box-sizing: border-box;
        display: flex;
        flex-direction: column;
        margin-bottom: 10rpx;
        background-color: aquamarine;
        border-radius: 8rpx;
    }

    .item_row {
        display: flex;
        flex-direction: row;
        align-items: center;
        padding: 0 20rpx;
        box-sizing: border-box;
    }

    .title {
        font-size: 32rpx;
        color: #333;
        font-weight: bold;
    }

    .value {
        font-size: 32rpx;
        color: brown;
        font-weight: bold;
    }

    .item_data {
        display: flex;
        width: 750rpx;
        background-color: burlywood;
        margin-top: 20rpx;
        padding: 10rpx 20rpx;
        box-sizing: border-box;
        flex-direction: column;
    }

    .read_title {
        font-size: 30rpx;
        color: aliceblue;
    }
</style>

9. 更多好用实惠插件

隐私、权限声明

1. 本插件需要申请的系统权限列表:

蓝牙

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: