- BluetoothManager: per-characteristic isolated channels (per-UUID read queue/timeout/dispatch, no cross-char write blocking), 10007 delayed retry for read/write, 300ms link settle after discovery, full tx/rx hex logging - McmCli/McmInfo/McmOta: command/response, TLV parse and OTA frame logs - home.vue: Auto refresh switch (polling off by default, no GATT interference with OTA), MTU-then-load sequencing - ota.vue: SAF file picker via plus.android (ACTION_GET_CONTENT) with Scanner whole-stream read + magic self-check + per-byte fallback; default path moved to _doc (Android 10+ scoped storage) - docs: change logs for 2026-09-04 protocol adaptation and 2026-09-05 field debugging; AGENTS.md updated
571 lines
18 KiB
TypeScript
571 lines
18 KiB
TypeScript
/**
|
||
* 蓝牙管理模块(基于 uni 内置 BLE API)
|
||
*
|
||
* 当前固件(mcm-ddc-ble V1.00.23)的可用 GATT 接口:
|
||
* - CLI 读写特征 ...c72e0003:写原始命令行(write-with-response,≤63B),
|
||
* 读同一特征分包取回输出文本(每片 ≤ att_mtu-2,0 长度包=结束)
|
||
* - 只读参数特征 ...c72e0004:读出全部信息项 TLV(同 INFO_RSP(all),
|
||
* 含 0x07 CUR_BANK 当前运行 bank)
|
||
* - OTA 特征 ...c72e0005:OTA 传输帧下行(write-with-response)→
|
||
* 读同一特征取回 OTA_RSP(读需轮询至非空),见 docs/ble_protocol.md §6.6
|
||
*
|
||
* 对外约定:
|
||
* - MyApiResult.type == 0 表示成功
|
||
* - 10000=连接失败 10001=已断开 10002=蓝牙未开/不可用 10003=无蓝牙权限
|
||
* - 10004/10005=服务/特征发现问题 10008=读写/MTU/RSSI 失败或超时
|
||
* - 连接状态变化通过 uni.$emit("bleState", state) 通知(0=已连接可用 10000=连接失败 10001=已断开)
|
||
*/
|
||
|
||
// CAIIC-MCM 设备 GATT(固件 mcm-ddc-ble app_rdtss.h 实际值,小写与 Android UUID.toString 格式一致)
|
||
export const MCM_SERVICE_UUID = "00002760-08c2-11e1-9073-0e8ac72e1001"
|
||
export const MCM_CLI_UUID = "00002760-08c2-11e1-9073-0e8ac72e0003" // 读写:写命令行 / 读输出文本
|
||
export const MCM_INFO_UUID = "00002760-08c2-11e1-9073-0e8ac72e0004" // 只读:设备参数 TLV(同 INFO_RSP 全部项)
|
||
export const MCM_OTA_UUID = "00002760-08c2-11e1-9073-0e8ac72e0005" // OTA:写传输帧 / 读 OTA_RSP(轮询至非空)
|
||
|
||
export type MyApiResult = {
|
||
type: number,
|
||
message: string,
|
||
data: any
|
||
}
|
||
|
||
export type BleScanDevice = {
|
||
name: string | null,
|
||
address: string
|
||
}
|
||
|
||
export type BleScanResult = {
|
||
device: BleScanDevice,
|
||
rssi: number
|
||
}
|
||
|
||
// ================= 模块状态 =================
|
||
|
||
let connectedDeviceId: string | null = null
|
||
let manualDisconnect = false
|
||
let btEnabled = false // 缓存的蓝牙开关状态(由 adapter 状态监听/操作结果维护)
|
||
let scanning = false
|
||
let scanTimeoutTimer = 0
|
||
let negotiatedMtu = 23 // 当前生效的 ATT MTU(默认 23,setMtu 成功后更新)
|
||
|
||
let stateListenerRegistered = false
|
||
let connListenerRegistered = false
|
||
let scanListenerRegistered = false
|
||
let valueListenerRegistered = false
|
||
|
||
// 读请求按特征值各自独立排队/跟踪(CLI/参数/OTA 各通道互不影响、可并行):
|
||
// uni 的读结果经 onBLECharacteristicValueChange 全局回调返回,按 characteristicId 分发;
|
||
// 同一特征上的读串行,不同特征各管各的超时与状态
|
||
type ReadReq = {
|
||
charId: string,
|
||
cb: (res: MyApiResult) => void,
|
||
retried: boolean // 10007 已重试过
|
||
}
|
||
const readQueues: { [key: string]: ReadReq[] } = {}
|
||
const readActiveMap: { [key: string]: ReadReq | null } = {}
|
||
const readTimers: { [key: string]: number } = {}
|
||
|
||
const READ_TIMEOUT_MS = 3000
|
||
|
||
function charKey(charId: string): string {
|
||
return charId.toUpperCase()
|
||
}
|
||
|
||
// 注册特征值变化监听(read 结果按特征值分发到各自的待决请求,幂等)
|
||
function ensureValueChangeListener() {
|
||
if (valueListenerRegistered) return
|
||
valueListenerRegistered = true
|
||
uni.onBLECharacteristicValueChange((res: any) => {
|
||
const charId = res.characteristicId as string
|
||
const key = charKey(charId)
|
||
if (readActiveMap[key] == null) return
|
||
const bytes = new Uint8Array(res.value as ArrayBuffer)
|
||
const arr = [] as number[]
|
||
for (let i = 0; i < bytes.length; i++) {
|
||
arr.push(bytes[i])
|
||
}
|
||
console.log("[MCM] rx " + charTag(charId) + " " + arr.length + "B: " + bytesHex(arr, 64))
|
||
finishRead(key, { type: 0, message: "read ok", data: arr } as MyApiResult)
|
||
})
|
||
}
|
||
|
||
function finishRead(key: string, res: MyApiResult) {
|
||
const req = readActiveMap[key]
|
||
readActiveMap[key] = null
|
||
clearTimeout(readTimers[key])
|
||
if (req != null) {
|
||
if (res.type != 0) {
|
||
console.log("[MCM] rx " + charTag(req.charId) + " FAIL: [" + res.type + "] " + res.message)
|
||
}
|
||
req.cb(res)
|
||
}
|
||
pumpReadQueue(key)
|
||
}
|
||
|
||
function pumpReadQueue(key: string) {
|
||
if (readActiveMap[key] != null) return
|
||
const id = connectedDeviceId
|
||
const queue = readQueues[key]
|
||
if (queue == null) return
|
||
const req = queue.shift()
|
||
if (req == null) return
|
||
if (id == null) {
|
||
req.cb({ type: 10001, message: "not connected", data: null } as MyApiResult)
|
||
pumpReadQueue(key)
|
||
return
|
||
}
|
||
// 链路稳定期内推迟发出(连接级一次性等待,不做跨特征阻塞)
|
||
const waitMs = linkReadyAt - Date.now()
|
||
if (waitMs > 0) {
|
||
queue.unshift(req)
|
||
setTimeout(() => { pumpReadQueue(key) }, waitMs)
|
||
return
|
||
}
|
||
readActiveMap[key] = req
|
||
console.log("[MCM] read " + charTag(req.charId) + " ...")
|
||
readTimers[key] = setTimeout(() => {
|
||
finishRead(key, { type: 10008, message: "read timeout", data: null } as MyApiResult)
|
||
}, READ_TIMEOUT_MS)
|
||
uni.readBLECharacteristicValue({
|
||
deviceId: id,
|
||
serviceId: MCM_SERVICE_UUID,
|
||
characteristicId: req.charId,
|
||
success: () => {},
|
||
fail: (err: any) => {
|
||
const msg: string = (err && err.errMsg) ? err.errMsg : "fail"
|
||
// 链路未稳定时读也会被误报 10007 property not support:补等后重试一次
|
||
if (!req.retried && msg.indexOf("property not support") >= 0) {
|
||
console.log("[MCM] read " + charTag(req.charId) + " 10007, wait " +
|
||
WRITE_10007_RETRY_MS + "ms and retry")
|
||
readActiveMap[key] = null
|
||
clearTimeout(readTimers[key])
|
||
req.retried = true
|
||
setTimeout(() => {
|
||
if (readQueues[key] == null) readQueues[key] = [] as ReadReq[]
|
||
readQueues[key].unshift(req)
|
||
pumpReadQueue(key)
|
||
}, WRITE_10007_RETRY_MS)
|
||
return
|
||
}
|
||
finishRead(key, failRes(10008, err))
|
||
}
|
||
})
|
||
}
|
||
|
||
function ok(message: string): MyApiResult {
|
||
return { type: 0, message: message, data: null } as MyApiResult
|
||
}
|
||
|
||
function failRes(type: number, err: any): MyApiResult {
|
||
return { type: type, message: (err && err.errMsg) ? err.errMsg : "fail", data: null } as MyApiResult
|
||
}
|
||
|
||
// uni BLE 错误 → 本模块 type 约定:蓝牙未开/不可用 10002,权限类 10003
|
||
function mapBleError(err: any): number {
|
||
const msg: string = (err && err.errMsg) ? err.errMsg : ""
|
||
if (msg.indexOf("ermission") >= 0) return 10003
|
||
return 10002
|
||
}
|
||
|
||
function uuidEq(a: string, b: string): boolean {
|
||
return a.toUpperCase() == b.toUpperCase()
|
||
}
|
||
|
||
// 字节数组转 hex 串(日志用,超过 maxLen 截断并标注总长度)
|
||
function bytesHex(arr: number[], maxLen: number): string {
|
||
let s = ""
|
||
const n = Math.min(arr.length, maxLen)
|
||
for (let i = 0; i < n; i++) {
|
||
s += ("0" + arr[i].toString(16)).slice(-2).toUpperCase() + " "
|
||
}
|
||
if (arr.length > maxLen) s += "..(" + arr.length + "B)"
|
||
return s
|
||
}
|
||
|
||
// 特征 UUID 短名(日志用,如 "...E0003")
|
||
function charTag(charId: string): string {
|
||
return "..." + charId.slice(-4).toUpperCase()
|
||
}
|
||
|
||
// 注册系统蓝牙开关状态监听并同步一次当前状态(幂等)
|
||
function ensureStateListener() {
|
||
if (stateListenerRegistered) return
|
||
stateListenerRegistered = true
|
||
uni.onBluetoothAdapterStateChange((res: any) => {
|
||
btEnabled = res.available
|
||
})
|
||
uni.getBluetoothAdapterState({
|
||
success: (res: any) => {
|
||
btEnabled = res.available
|
||
},
|
||
fail: () => {}
|
||
})
|
||
}
|
||
|
||
// 注册连接状态监听(幂等):意外断开时广播 bleState 10001
|
||
function ensureConnListener() {
|
||
if (connListenerRegistered) return
|
||
connListenerRegistered = true
|
||
uni.onBLEConnectionStateChange((res: any) => {
|
||
if (res.connected) {
|
||
connectedDeviceId = res.deviceId
|
||
console.log("[MCM] connected: " + res.deviceId)
|
||
} else {
|
||
console.log("[MCM] disconnected: " + res.deviceId + (manualDisconnect ? " (manual)" : ""))
|
||
const wasConnected = connectedDeviceId != null && connectedDeviceId == res.deviceId
|
||
connectedDeviceId = null
|
||
negotiatedMtu = 23
|
||
linkReadyAt = 0
|
||
// 断连后所有特征的待决/排队读请求立即失败(逐通道清理)
|
||
const keys = [] as string[]
|
||
for (const k in readActiveMap) keys.push(k)
|
||
for (const k in readQueues) {
|
||
if (keys.indexOf(k) < 0) keys.push(k)
|
||
}
|
||
for (let i = 0; i < keys.length; i++) {
|
||
const k = keys[i]
|
||
if (readActiveMap[k] != null) {
|
||
finishRead(k, { type: 10001, message: "disconnected", data: null } as MyApiResult)
|
||
}
|
||
// finishRead→pumpReadQueue 会以 not connected 依次排空该通道队列;
|
||
// 对在稳定期推迟的队列(无待决请求)也主动触发一次排空
|
||
pumpReadQueue(k)
|
||
}
|
||
if (wasConnected && !manualDisconnect) {
|
||
uni.$emit("bleState", 10001)
|
||
}
|
||
manualDisconnect = false
|
||
}
|
||
})
|
||
}
|
||
|
||
// ================= 适配器 =================
|
||
|
||
// 手机蓝牙是否打开 true-打开 false-未打开
|
||
export function isEnabled(): boolean {
|
||
ensureStateListener()
|
||
return btEnabled
|
||
}
|
||
|
||
// 打开蓝牙(初始化蓝牙适配器;蓝牙关闭/无权限时走 fail)
|
||
export function openBluetooth(cb: (res: MyApiResult) => void) {
|
||
ensureStateListener()
|
||
uni.openBluetoothAdapter({
|
||
success: () => {
|
||
btEnabled = true
|
||
cb(ok("bluetooth adapter opened"))
|
||
},
|
||
fail: (err: any) => {
|
||
cb(failRes(mapBleError(err), err))
|
||
}
|
||
})
|
||
}
|
||
|
||
// ================= 扫描 =================
|
||
|
||
// 开始蓝牙扫描:每个发现的设备回调一次 {type:0, data: BleScanResult};超时自动停止
|
||
export function startScan(timeoutMs: number, cb: (res: MyApiResult) => void) {
|
||
ensureStateListener()
|
||
uni.openBluetoothAdapter({
|
||
success: () => {
|
||
btEnabled = true
|
||
if (scanning) return
|
||
if (!scanListenerRegistered) {
|
||
scanListenerRegistered = true
|
||
uni.onBluetoothDeviceFound((res: any) => {
|
||
const devices = res.devices as any[]
|
||
for (let i = 0; i < devices.length; i++) {
|
||
const d = devices[i]
|
||
let name: string | null = d.name as string
|
||
if (name == null || name.length == 0) {
|
||
const ln = (d.localName ?? "") as string
|
||
name = ln.length > 0 ? ln : null
|
||
}
|
||
const sr: BleScanResult = {
|
||
device: { name: name, address: d.deviceId } as BleScanDevice,
|
||
rssi: d.RSSI as number
|
||
} as BleScanResult
|
||
cb({ type: 0, message: "device found", data: sr } as MyApiResult)
|
||
}
|
||
})
|
||
}
|
||
scanning = true
|
||
uni.startBluetoothDevicesDiscovery({
|
||
allowDuplicatesKey: false,
|
||
success: () => {},
|
||
fail: (err: any) => {
|
||
scanning = false
|
||
cb(failRes(mapBleError(err), err))
|
||
}
|
||
})
|
||
if (scanTimeoutTimer != 0) {
|
||
clearTimeout(scanTimeoutTimer)
|
||
}
|
||
scanTimeoutTimer = setTimeout(() => {
|
||
stopScan()
|
||
}, timeoutMs)
|
||
},
|
||
fail: (err: any) => {
|
||
cb(failRes(mapBleError(err), err))
|
||
}
|
||
})
|
||
}
|
||
|
||
// 停止蓝牙扫描
|
||
export function stopScan() {
|
||
if (scanTimeoutTimer != 0) {
|
||
clearTimeout(scanTimeoutTimer)
|
||
scanTimeoutTimer = 0
|
||
}
|
||
if (!scanning) return
|
||
scanning = false
|
||
uni.stopBluetoothDevicesDiscovery({
|
||
success: () => {},
|
||
fail: () => {}
|
||
})
|
||
}
|
||
|
||
// ================= 连接 =================
|
||
|
||
// 蓝牙是否已连接
|
||
export function isConnected(): boolean {
|
||
return connectedDeviceId != null
|
||
}
|
||
|
||
// 获取已连接设备id(未连接返回空串)
|
||
export function getConnectMac(): string {
|
||
return connectedDeviceId ?? ""
|
||
}
|
||
|
||
// 连接蓝牙
|
||
export function connect(mac: string, autoReconnect: boolean, cb: (res: MyApiResult) => void) {
|
||
ensureStateListener()
|
||
ensureConnListener()
|
||
manualDisconnect = false
|
||
uni.createBLEConnection({
|
||
deviceId: mac,
|
||
success: () => {
|
||
connectedDeviceId = mac
|
||
cb(ok("connected"))
|
||
},
|
||
fail: (err: any) => {
|
||
cb(failRes(10000, err))
|
||
}
|
||
})
|
||
}
|
||
|
||
// 断开蓝牙
|
||
export function disConnect() {
|
||
const id = connectedDeviceId
|
||
manualDisconnect = true
|
||
connectedDeviceId = null
|
||
if (id != null) {
|
||
uni.closeBLEConnection({
|
||
deviceId: id,
|
||
success: () => {},
|
||
fail: () => {}
|
||
})
|
||
}
|
||
}
|
||
|
||
// ================= 服务发现 =================
|
||
|
||
// 连接后扫描服务:确认 MCM 服务及 CLI 读写/参数只读特征存在
|
||
export function scanServices(cb: (res: MyApiResult) => void) {
|
||
const id = connectedDeviceId
|
||
if (id == null) {
|
||
cb({ type: 10001, message: "not connected", data: null } as MyApiResult)
|
||
return
|
||
}
|
||
uni.getBLEDeviceServices({
|
||
deviceId: id,
|
||
success: (res: any) => {
|
||
const services = res.services as any[]
|
||
let found = false
|
||
for (let i = 0; i < services.length; i++) {
|
||
if (uuidEq(services[i].uuid as string, MCM_SERVICE_UUID)) {
|
||
found = true
|
||
break
|
||
}
|
||
}
|
||
if (!found) {
|
||
cb({ type: 10004, message: "MCM service not found", data: null } as MyApiResult)
|
||
return
|
||
}
|
||
uni.getBLEDeviceCharacteristics({
|
||
deviceId: id as string,
|
||
serviceId: MCM_SERVICE_UUID,
|
||
success: (resCh: any) => {
|
||
const chars = resCh.characteristics as any[]
|
||
let hasCli = false
|
||
let hasInfo = false
|
||
let hasOta = false
|
||
for (let i = 0; i < chars.length; i++) {
|
||
const cu = chars[i].uuid as string
|
||
// 打印手机侧实际发现的特征及属性(排查 GATT 缓存/权限位用)
|
||
const props = chars[i].properties as any
|
||
console.log("[MCM] char " + cu +
|
||
" read=" + (props && props.read) +
|
||
" write=" + (props && props.write) +
|
||
" writeNoRsp=" + (props && props.writeNoResponse) +
|
||
" notify=" + (props && props.notify))
|
||
if (uuidEq(cu, MCM_CLI_UUID)) hasCli = true
|
||
if (uuidEq(cu, MCM_INFO_UUID)) hasInfo = true
|
||
if (uuidEq(cu, MCM_OTA_UUID)) hasOta = true
|
||
}
|
||
if (!hasCli || !hasInfo || !hasOta) {
|
||
cb({ type: 10005, message: "MCM cli/info/ota characteristic not found", data: null } as MyApiResult)
|
||
return
|
||
}
|
||
// 链路稳定期:首个 GATT 读写操作会等到此刻之后发出(安卓时序兼容)
|
||
linkReadyAt = Date.now() + GATT_SETTLE_MS
|
||
cb(ok("services ready"))
|
||
},
|
||
fail: (err: any) => {
|
||
cb(failRes(10005, err))
|
||
}
|
||
})
|
||
},
|
||
fail: (err: any) => {
|
||
cb(failRes(10004, err))
|
||
}
|
||
})
|
||
}
|
||
|
||
// ================= 读写 =================
|
||
|
||
// 各特征值的读写相互独立、可并行:不做跨特征的全局串行化。
|
||
// 仅保留两个连接级保护:服务发现后的短暂稳定期(安卓链路未稳时
|
||
// 读会无声丢失、写会误报 10007),以及写 10007 的延时重试。
|
||
const GATT_SETTLE_MS = 300 // scanServices 成功后首个 GATT 操作前的稳定等待
|
||
const WRITE_10007_RETRY_MS = 1600 // 10007 重试前的补等时长
|
||
|
||
let linkReadyAt = 0 // 链路就绪时刻(scanServices 成功时设置,断连清零)
|
||
|
||
// 写特征(通用,write-with-response):data 为原始字节
|
||
export function writeChar(charId: string, data: number[], cb: (res: MyApiResult) => void) {
|
||
const id = connectedDeviceId
|
||
if (id == null) {
|
||
cb({ type: 10001, message: "not connected", data: null } as MyApiResult)
|
||
return
|
||
}
|
||
const attempt = (isRetry: boolean) => {
|
||
if (connectedDeviceId == null) {
|
||
cb({ type: 10001, message: "disconnected", data: null } as MyApiResult)
|
||
return
|
||
}
|
||
const waitMs = linkReadyAt - Date.now()
|
||
if (waitMs > 0) {
|
||
setTimeout(() => { attempt(isRetry) }, waitMs)
|
||
return
|
||
}
|
||
const buf = new Uint8Array(data.length)
|
||
for (let i = 0; i < data.length; i++) {
|
||
buf[i] = data[i] & 0xFF
|
||
}
|
||
uni.writeBLECharacteristicValue({
|
||
deviceId: id,
|
||
serviceId: MCM_SERVICE_UUID,
|
||
characteristicId: charId,
|
||
value: buf.buffer,
|
||
writeType: "write",
|
||
success: () => {
|
||
console.log("[MCM] tx " + charTag(charId) + " " + data.length + "B ok: " + bytesHex(data, 48))
|
||
cb(ok("write ok"))
|
||
},
|
||
fail: (err: any) => {
|
||
const msg: string = (err && err.errMsg) ? err.errMsg : "fail"
|
||
// 部分安卓机型链路未稳定时误报 10007 property not support:补等后重试一次
|
||
if (!isRetry && msg.indexOf("property not support") >= 0) {
|
||
console.log("[MCM] tx " + charTag(charId) + " 10007, wait " +
|
||
WRITE_10007_RETRY_MS + "ms and retry")
|
||
setTimeout(() => { attempt(true) }, WRITE_10007_RETRY_MS)
|
||
return
|
||
}
|
||
console.log("[MCM] tx " + charTag(charId) + " FAIL: " + msg)
|
||
cb(failRes(10008, err))
|
||
}
|
||
})
|
||
}
|
||
attempt(false)
|
||
}
|
||
|
||
// 写 CLI 特征(write-with-response):data 为原始命令行字节(≤63B,且不超 mtu-3)
|
||
export function writeCliChar(data: number[], cb: (res: MyApiResult) => void) {
|
||
writeChar(MCM_CLI_UUID, data, cb)
|
||
}
|
||
|
||
// 写 OTA 特征(write-with-response):data 为一帧完整 0xCA OTA 传输帧;
|
||
// 应答经 readChar(MCM_OTA_UUID) 轮询取回(读可能先返回空,需读到非空为止)
|
||
export function writeOtaChar(data: number[], cb: (res: MyApiResult) => void) {
|
||
writeChar(MCM_OTA_UUID, data, cb)
|
||
}
|
||
|
||
// 读特征(通用):结果经 onBLECharacteristicValueChange 按 characteristicId 分发,
|
||
// 同一特征上的读串行排队,不同特征各自独立、可并行;每请求 3s 超时;
|
||
// type==0 时 data 为 number[]
|
||
export function readChar(charId: string, cb: (res: MyApiResult) => void) {
|
||
ensureValueChangeListener()
|
||
const key = charKey(charId)
|
||
if (readQueues[key] == null) readQueues[key] = [] as ReadReq[]
|
||
readQueues[key].push({ charId: charId, cb: cb, retried: false } as ReadReq)
|
||
pumpReadQueue(key)
|
||
}
|
||
|
||
// 读取设备参数特征(只读 MCM_INFO_UUID,内容为全部信息项 TLV)
|
||
export function readInfoChar(cb: (res: MyApiResult) => void) {
|
||
readChar(MCM_INFO_UUID, cb)
|
||
}
|
||
|
||
// ================= 其他 =================
|
||
|
||
// 设置 mtu(仅 App 端有效;其他平台回调失败,调用方保持默认 20 字节上限)
|
||
export function setMtu(len: number, cb: (res: MyApiResult) => void) {
|
||
const id = connectedDeviceId
|
||
if (id == null) {
|
||
cb({ type: 10001, message: "not connected", data: null } as MyApiResult)
|
||
return
|
||
}
|
||
// #ifdef APP-PLUS
|
||
uni.setBLEMTU({
|
||
deviceId: id,
|
||
mtu: len,
|
||
success: (res: any) => {
|
||
// 部分平台(实测有的 Android 机型/uni 版本)成功回调不带 mtu 字段:
|
||
// 此时按请求值记录(设备侧每连接也会主动发起 MTU=247 交换)
|
||
const m = res ? (res.mtu as number) : (undefined as any)
|
||
negotiatedMtu = (typeof m == "number" && m >= 23) ? m : len
|
||
cb({ type: 0, message: "mtu " + negotiatedMtu, data: null } as MyApiResult)
|
||
},
|
||
fail: (err: any) => {
|
||
cb(failRes(10008, err))
|
||
}
|
||
})
|
||
// #endif
|
||
// #ifndef APP-PLUS
|
||
cb({ type: 10008, message: "set mtu not supported on this platform", data: null } as MyApiResult)
|
||
// #endif
|
||
}
|
||
|
||
// 当前协商的 ATT MTU(未调用过 setMtu 或协商失败时为默认 23)
|
||
export function getMtu(): number {
|
||
return negotiatedMtu
|
||
}
|
||
|
||
// 读取 rssi 信号 需要蓝牙已经连接
|
||
export function readRssi(cb: (res: MyApiResult) => void) {
|
||
const id = connectedDeviceId
|
||
if (id == null) {
|
||
cb({ type: 10001, message: "not connected", data: null } as MyApiResult)
|
||
return
|
||
}
|
||
uni.getBLEDeviceRSSI({
|
||
deviceId: id,
|
||
success: (res: any) => {
|
||
cb({ type: 0, message: "rssi", data: res.RSSI } as MyApiResult)
|
||
},
|
||
fail: (err: any) => {
|
||
cb(failRes(10008, err))
|
||
}
|
||
})
|
||
}
|