Ent*_*ain 8 android bluetooth ios bluetooth-lowenergy ionic-framework
我们目前正在尝试实现将图像从移动设备(在本例中为iPhone)传输到桌面应用程序的过程。我们已经尝试了蓝牙串行插件,该插件可在Android上正常运行,但在扫描桌面应用程序时未列出任何设备。
为了涵盖iOS支持(AFAIK iOS仅支持BluetoothLE),我们重新实现了桌面应用程序以使用BluetoothLE并表现为外围设备。另外,我们更改了Ionic应用程序以使用BLE插件。
现在,BluetoothLE仅支持传输大小为20 Byte的包,而我们的映像大约为500kb。因此,我们显然可以将图像拆分为多个块,并使用以下函数进行传输(取自该要点):
function writeLargeData(buffer) {
console.log('writeLargeData', buffer.byteLength, 'bytes in',MAX_DATA_SEND_SIZE, 'byte chunks.');
var chunkCount = Math.ceil(buffer.byteLength / MAX_DATA_SEND_SIZE);
var chunkTotal = chunkCount;
var index = 0;
var startTime = new Date();
var transferComplete = function () {
console.log("Transfer Complete");
}
var sendChunk = function () {
if (!chunkCount) {
transferComplete();
return; // so we don't send an empty buffer
}
console.log('Sending data chunk', chunkCount + '.');
var chunk = buffer.slice(index, index + MAX_DATA_SEND_SIZE);
index += MAX_DATA_SEND_SIZE;
chunkCount--;
ble.write(
device_id,
service_uuid,
characteristic_uuid,
chunk,
sendChunk, // success callback - call sendChunk() (recursive)
function(reason) { // error callback
console.log('Write failed ' + reason);
}
)
}
// send the first chunk
sendChunk();
}
Run Code Online (Sandbox Code Playgroud)
对于我们来说,这仍然意味着我们将不得不发射大约2万5 千次的传输,我认为这将需要很长时间才能完成。现在,我想知道为什么通过蓝牙进行的数据传输受到限制。
如果你想尝试 L2CAP,你可以像这样修改你的 Central 桌面应用程序:
private let characteristicUUID = CBUUID(string: CBUUIDL2CAPPSMCharacteristicString)
...
Run Code Online (Sandbox Code Playgroud)
然后广告发布一个 L2CAP 频道:
let service = CBMutableService(type: peripheralUUID, primary: true)
let properties: CBCharacteristicProperties = [.read, .indicate]
let permissions: CBAttributePermissions = [.readable]
let characteristic = CBMutableCharacteristic(type: characteristicUUID, properties: properties, value: nil, permissions: permissions)
self.characteristic = characteristic
service.characteristics = [characteristic]
self.manager.add(service)
self.manager.publishL2CAPChannel(withEncryption: false)
let data = [CBAdvertisementDataLocalNameKey : "Peripherial-42", CBAdvertisementDataServiceUUIDsKey: [peripheralUUID]] as [String : Any]
self.manager.startAdvertising(data)
Run Code Online (Sandbox Code Playgroud)
在你的
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
Run Code Online (Sandbox Code Playgroud)
各自的
func peripheralManager(_ peripheral: CBPeripheralManager, didPublishL2CAPChannel PSM: CBL2CAPPSM, error: Error?) {
Run Code Online (Sandbox Code Playgroud)
提供 PSM 值(= 套接字句柄类型 (UInt16),用于蓝牙流连接):
let data = withUnsafeBytes(of: PSM) { Data($0) }
if let characteristic = self.characteristic {
characteristic.value = data
self.manager.updateValue(data, for: characteristic, onSubscribedCentrals: self.subscribedCentrals)
}
Run Code Online (Sandbox Code Playgroud)
终于在
func peripheralManager(_ peripheral: CBPeripheralManager, didOpen channel: CBL2CAPChannel?, error: Error?)
Run Code Online (Sandbox Code Playgroud)
打开一个输入流:
channel.inputStream.delegate = self
channel.inputStream.schedule(in: RunLoop.current, forMode: .default)
channel.inputStream.open()
Run Code Online (Sandbox Code Playgroud)
代表可能看起来像这样:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.hasBytesAvailable:
if let stream = aStream as? InputStream {
...
//buffer is some UnsafeMutablePointer<UInt8>
let read = stream.read(buffer, maxLength: capacity)
print("\(read) bytes read")
}
case ...
}
Run Code Online (Sandbox Code Playgroud)
具有中心角色的 iOS 应用
假设您的 iOS 代码中有类似的内容:
func sendImage(imageData: Data) {
self.manager = CBCentralManager(delegate: self, queue: nil)
self.imageData = imageData
self.bytesToWrite = imageData.count
NSLog("start")
}
Run Code Online (Sandbox Code Playgroud)
然后你可以在你的 iOS 客户端上修改你的外设来使用 L2Cap 通道,如下所示:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
...
if let characteristicValue = characteristic.value {
let psm = characteristicValue.withUnsafeBytes {
$0.load(as: UInt16.self)
}
print("using psm \(psm) for l2cap channel!")
peripheral.openL2CAPChannel(psm)
}
}
Run Code Online (Sandbox Code Playgroud)
一旦您收到已打开频道的通知,请在其上打开输出流:
func peripheral(_ peripheral: CBPeripheral, didOpen channel: CBL2CAPChannel?, error: Error?)
...
channel.outputStream.delegate = self.streamDelegate
channel.outputStream.schedule(in: RunLoop.current, forMode: .default)
channel.outputStream.open()
Run Code Online (Sandbox Code Playgroud)
您提供的流委托可能如下所示:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.hasSpaceAvailable:
if let stream = aStream as? OutputStream, let imageData = self.imageData {
if self.bytesToWrite > 0 {
let bytesWritten = imageData.withUnsafeBytes {
stream.write(
$0.advanced(by: totalBytes),
maxLength: self.bytesToWrite
)
}
self.bytesToWrite -= bytesWritten
self.totalBytes += bytesWritten
print("\(bytesWritten) bytes written, \(bytesToWrite) remain")
} else {
NSLog("finished")
}
}
case ...
Run Code Online (Sandbox Code Playgroud)
2017 年有一个很酷的 WWDC 视频,Core Bluetooth 中的新功能,请参见此处https://developer.apple.com/videos/play/wwdc2017/712/
在 14:45 左右,它开始讨论 L2Cap 通道的工作方式。
28:47,Get the Most of the Core Bluetooth主题开始,详细讨论性能相关的东西。这可能正是你感兴趣的。
最后,在 37:59,您将看到以 kbps 为单位的各种可能的吞吐量。根据幻灯片上显示的数据,L2CAP + EDL(扩展数据长度)+ 15ms 间隔的最大可能速度为 394 kbps。
请看一下这个评论
以下片段摘自那里
ble.requestMtu(yourDeviceId, 512, () => {
console.log('MTU Size ok.');
}, error => {
console.log('MTU Size failed.');
});
Run Code Online (Sandbox Code Playgroud)
这表明您需要在连接后请求 Mtu,然后我认为您可以将消息分成 512 字节而不是 20 字节的块。
他们针对 Android 特定问题做了此操作
| 归档时间: |
|
| 查看次数: |
311 次 |
| 最近记录: |