[Unity插件使用心得] 适用于iOS,tvOS和Android的蓝牙LE
unity使用【适用于iOS,tvOS和Android的蓝牙LE】插件 与传感器进行连接时碰到的一些问题和解决办法。
以示例为例,主要的功能如下:
设备信息、初始化插件
public string DeviceName = "ledbtn";
public string ServiceUUID = "A9E90000-194C-4523-A473-5FDF36AA4D20";
public string LedUUID = "A9E90001-194C-4523-A473-5FDF36AA4D20";
public string ButtonUUID = "A9E90002-194C-4523-A473-5FDF36AA4D20";
void StartProcess()
{
Reset();
BluetoothLEHardwareInterface.Initialize(true, false, () =>
{
SetState(States.Scan, 0.1f);
}, (error) =>
{
StatusMessage = "Error during initialize: " + error;
});
}
扫描、连接、订阅
void Update()
{
if (_timeout > 0f)
{
_timeout -= Time.deltaTime;
if (_timeout <= 0f)
{
_timeout = 0f;
switch (_state)
{
case States.None:
break;
case States.Scan:
StatusMessage = "Scanning for " + DeviceName;
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null, (address, name) =>
{
// if your device does not advertise the rssi and manufacturer specific data
// then you must use this callback because the next callback only gets called
// if you have manufacturer specific data
if (!_rssiOnly)
{
if (name.Contains(DeviceName))
{
StatusMessage = "Found " + name;
// found a device with the name we want
// this example does not deal with finding more than one
_deviceAddress = address;
SetState(States.Connect, 0.5f);
}
}
}, (address, name, rssi, bytes) =>
{
// use this one if the device responses with manufacturer specific data and the rssi
if (name.Contains(DeviceName))
{
StatusMessage = "Found " + name;
if (_rssiOnly)
{
_rssi = rssi;
}
else
{
// found a device with the name we want
// this example does not deal with finding more than one
_deviceAddress = address;
SetState(States.Connect, 0.5f);
}
}
}, _rssiOnly); // this last setting allows RFduino to send RSSI without having manufacturer data
if (_rssiOnly)
SetState(States.ScanRSSI, 0.5f);
break;
case States.ScanRSSI:
break;
case States.ReadRSSI:
StatusMessage = $"Call Read RSSI";
BluetoothLEHardwareInterface.ReadRSSI(_deviceAddress, (address, rssi) =>
{
StatusMessage = $"Read RSSI: {rssi}";
});
SetState(States.ReadRSSI, 2f);
break;
case States.Connect:
StatusMessage = "Connecting...";
// set these flags
_foundButtonUUID = false;
_foundLedUUID = false;
// note that the first parameter is the address, not the name. I have not fixed this because
// of backwards compatiblity.
// also note that I am note using the first 2 callbacks. If you are not looking for specific characteristics you can use one of
// the first 2, but keep in mind that the device will enumerate everything and so you will want to have a timeout
// large enough that it will be finished enumerating before you try to subscribe or do any other operations.
BluetoothLEHardwareInterface.ConnectToPeripheral(_deviceAddress, null, null, (address, serviceUUID, characteristicUUID) =>
{
StatusMessage = "Connected...";
BluetoothLEHardwareInterface.StopScan();
if (IsEqual(serviceUUID, ServiceUUID))
{
StatusMessage = "Found Service UUID";
_foundButtonUUID = _foundButtonUUID || IsEqual(characteristicUUID, ButtonUUID);
_foundLedUUID = _foundLedUUID || IsEqual(characteristicUUID, LedUUID);
// if we have found both characteristics that we are waiting for
// set the state. make sure there is enough timeout that if the
// device is still enumerating other characteristics it finishes
// before we try to subscribe
if (_foundButtonUUID && _foundLedUUID)
{
_connected = true;
SetState(States.RequestMTU, 2f);
}
}
});
break;
case States.RequestMTU:
StatusMessage = "Requesting MTU";
BluetoothLEHardwareInterface.RequestMtu(_deviceAddress, 185, (address, newMTU) =>
{
StatusMessage = "MTU set to " + newMTU.ToString();
SetState(States.Subscribe, 0.1f);
});
break;
case States.Subscribe:
StatusMessage = "Subscribing to characteristics...";
BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress(_deviceAddress, ServiceUUID, ButtonUUID, (notifyAddress, notifyCharacteristic) =>
{
StatusMessage = "Waiting for user action (1)...";
_state = States.None;
// read the initial state of the button
BluetoothLEHardwareInterface.ReadCharacteristic(_deviceAddress, ServiceUUID, ButtonUUID, (characteristic, bytes) =>
{
ProcessButton(bytes);
});
SetState(States.ReadRSSI, 1f);
}, (address, characteristicUUID, bytes) =>
{
if (_state != States.None)
{
// some devices do not properly send the notification state change which calls
// the lambda just above this one so in those cases we don't have a great way to
// set the state other than waiting until we actually got some data back.
// The esp32 sends the notification above, but if yuor device doesn't you would have
// to send data like pressing the button on the esp32 as the sketch for this demo
// would then send data to trigger this.
StatusMessage = "Waiting for user action (2)...";
SetState(States.ReadRSSI, 1f);
}
// we received some data from the device
ProcessButton(bytes);
});
break;
case States.Unsubscribe:
BluetoothLEHardwareInterface.UnSubscribeCharacteristic(_deviceAddress, ServiceUUID, ButtonUUID, null);
SetState(States.Disconnect, 4f);
break;
case States.Disconnect:
StatusMessage = "Commanded disconnect.";
if (_connected)
{
BluetoothLEHardwareInterface.DisconnectPeripheral(_deviceAddress, (address) =>
{
StatusMessage = "Device disconnected";
BluetoothLEHardwareInterface.DeInitialize(() =>
{
_connected = false;
_state = States.None;
});
});
}
else
{
BluetoothLEHardwareInterface.DeInitialize(() =>
{
_state = States.None;
});
}
break;
}
}
}
}
写入
void SendByte(byte value)
{
byte[] data = { value };
BluetoothLEHardwareInterface.WriteCharacteristic(_deviceAddress, ServiceUUID, LedUUID, data, data.Length, true, (characteristicUUID) =>
{
BluetoothLEHardwareInterface.Log("Write Succeeded");
});
}
其中碰到几个问题
①扫描到蓝牙后连接不上
②安卓能正常订阅但是IOS订阅后没有应用回调函数
扫描到蓝牙后连接不上
一开始我一直用的StartingExample来改,一直没有成功,后面换了个示例MultipleLevels,一下子就成功了。仔细看了下他们的区别,只是扫描设备时少了一个回调函数,减少一个回调后续连接就成功了,问作者也只是说明了下这两个回调是干嘛的,具体导致连不上的原因我也没搞懂~
BluetoothLEHardwareInterface.Initialize (true, false, () => {
FoundDeviceListScript.DeviceAddressList = new List<DeviceObject> ();
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices (null, (address, name) => {
FoundDeviceListScript.DeviceAddressList.Add (new DeviceObject (address, name));
}, null);
}, (error) => {
BluetoothLEHardwareInterface.Log ("BLE Error: " + error);
});
StartingExample修改如下
//减少一个回调
case States.Scan:
StatusMessage = "Scanning for " + DeviceName;
BluetoothLEHardwareInterface.ScanForPeripheralsWithServices(null, (address, name) =>
{
if (name.Contains(DeviceName))
{
if (adressList.Contains(name))
{
return;
}
adressList.Add(name);
_state = States.None;
StatusMessage = "Found " + name;
_deviceAddress = address;
deviceObjects.Add(new DeviceObject(address, name));
Debug.Log($"_deviceadress1:{_deviceAddress}");
SetState(States.Connect, 2f);
}
}, null);
作者原话:
There are 2 callbacks that can be used to detect scanned devices. The first one is for devices that are not advertising a Manufacturer specific packet of type 0xFF. The other one is for those that have that packet in their advertising.
我:好吧,不纠结,能连就行~
安卓能正常订阅但是IOS订阅后没有应用回调函数
一开始打包在安卓机上跑时,能够订阅到传感器数据,转换完在UI上显示出来。
然后去了IOS上,打包后有个非常奇怪的现象,xcode 的log上是有我订阅到了数据的,但是UI上却显示不出来,打log看了,发现没有进入回调函数。
BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress(_deviceAddress, ServiceUUID, ButtonUUID, (notifyAddress, notifyCharacteristic) =>
{
StatusMessage = "Waiting for user action (1)...";
_state = States.None;
// read the initial state of the button
BluetoothLEHardwareInterface.ReadCharacteristic(_deviceAddress, ServiceUUID, ButtonUUID, (characteristic, bytes) =>
{
ProcessButton(bytes);
});
SetState(States.ReadRSSI, 1f);
}, (address, characteristicUUID, bytes) =>
{
//订阅后数据更新时都会传到这块ProcessButton(bytes)就是自己根据设备和需求来处理数据
if (_state != States.None)
{
StatusMessage = "Waiting for user action (2)...";
SetState(States.ReadRSSI, 1f);
}
// we received some data from the device
ProcessButton(bytes);
});
找了半天定位到BluetoothDeviceScript这
BluetoothDeviceScript
public void OnBluetoothData (string deviceAddress, string characteristic, string base64Data)
{
if (base64Data != null)
{
byte[] bytes = System.Convert.FromBase64String (base64Data);
if (bytes.Length > 0)
{
deviceAddress = deviceAddress.ToUpper ();
characteristic = characteristic.ToUpper ();
#if UNITY_IOS
if (BLEStandardUUIDs.ContainsKey(characteristic))
characteristic = BLEStandardUUIDs[characteristic];
#endif
BluetoothLEHardwareInterface.Log ("Device: " + deviceAddress + " Characteristic Received: " + characteristic);
string byteString = "";
foreach (byte b in bytes)
byteString += string.Format ("{0:X2}", b);
BluetoothLEHardwareInterface.Log (byteString);
if (DidUpdateCharacteristicValueAction != null && DidUpdateCharacteristicValueAction.ContainsKey (deviceAddress))
{
Debug.Log($"part0.1");
var characteristicAction = DidUpdateCharacteristicValueAction[deviceAddress];
#if UNITY_ANDROID
characteristic = characteristic.ToLower ();
#endif
if (characteristicAction != null && characteristicAction.ContainsKey (characteristic))
{
Debug.Log($"part0.2");
var action = characteristicAction[characteristic];
if (action != null)
{
Debug.Log($"part0.3");
action(characteristic, bytes);
}
}
}
if (DidUpdateCharacteristicValueWithDeviceAddressAction != null && DidUpdateCharacteristicValueWithDeviceAddressAction.ContainsKey (deviceAddress))
{
var characteristicAction = DidUpdateCharacteristicValueWithDeviceAddressAction[deviceAddress];
#if UNITY_ANDROID
characteristic = characteristic.ToLower ();
#endif
Debug.Log($"part1.1");
if (characteristicAction != null && characteristicAction.ContainsKey (characteristic))
{
Debug.Log($"part1.2");
var action = characteristicAction[characteristic];
if (action != null)
{
Debug.Log($"part1.3");
action(deviceAddress, characteristic, bytes);
}
}
}
}
}
}
log
//2023-12-11 16:52:59.670000+0800 ble[24085:924414] | DidUpdateNotificationStateForCharacteristic| C1523F8E-C27F-AFB7-4FAA-ECF0FB1013F7| AE02
//2023-12-11 16:52:59.670520+0800 ble[24085:924414] | DidUpdateValueForCharacteristic| C1523F8E-C27F-AFB7-4FAA-ECF0FB1013F7| AE02| EWcAotRFAQAAAAAAAAAAEQABCAAAAAAAAP9/hgAFAPL/VQADAPf/
//2023-12-11 16:52:59.672097+0800 ble[24085:924414] Device: C1523F8E-C27F-AFB7-4FAA-ECF0FB1013F7 Characteristic Received: AE02
//2023-12-11 16:52:59.675121+0800 ble[24085:924414] 116700A2D44501000000000000000011000108000000000000FF7F86000500F2FF55000300F7FF
//part1.1
//BluetoothDeviceScript:OnBluetoothData(String, String, String)
//2023-12-11 16:52:59.703305+0800 ble[24085:924414] | DidUpdateValueForCharacteristic| C1523F8E-C27F-AFB7-4FAA-ECF0FB1013F7| AE02| EWcAw9RFAQAAAAAAAAAAEQABCAAAAAAAAP9/hgAFAPL/VQADAPf/
//2023-12-11 16:52:59.703723+0800 ble[24085:924414] Device: C1523F8E-C27F-AFB7-4FAA-ECF0FB1013F7 Characteristic Received: AE02
//2023-12-11 16:52:59.704003+0800 ble[24085:924414] 116700C3D44501000000000000000011000108000000000000FF7F86000500F2FF55000300F7FF
//part1.1
//BluetoothDeviceScript:OnBluetoothData(String, String, String)
//2023-12-11 16:52:59.769981+0800 ble[24085:924414] | DidUpdateNotificationStateForCharacteristic| C1523F8E-C27F-AFB7-4FAA-ECF0FB1013F7| AE02
//2023-12-11 16:52:59.770511+0800 ble[24085:924414] | DidUpdateValueForCharacteristic| C1523F8E-C27F-AFB7-4FAA-ECF0FB1013F7| AE02| EWcA5NRFAQAAAAAAAAAAEQABCAAAAAAAAP9/hgAFAPL/VQADAPf/
//2023-12-11 16:52:59.770775+0800 ble[24085:924414] Device: C1523F8E-C27F-AFB7-4FAA-ECF0FB1013F7 Characteristic Received: AE02
//2023-12-11 16:52:59.771055+0800 ble[24085:924414] 116700E4D44501000000000000000011000108000000000000FF7F86000500F2FF55000300F7FF
//part1.1
//BluetoothDeviceScript:OnBluetoothData(String, String, String)
原来是订阅的UUID的问题
安卓用的128bit 小写UUID 而 IOS回调给的UUID是16bit大写UUID
然后就很没问题了
public string ServiceUUID = "A9E90000-194C-4523-A473-5FDF36AA4D20";
public string LedUUID = "A9E90001-194C-4523-A473-5FDF36AA4D20";
public string ButtonUUID = "A9E90002-194C-4523-A473-5FDF36AA4D20";
//改为第一段的后4位数字,有字母建议大写
private string imUUIDs = "0000";
private string imUUID1 = "0001";
private string imUUID2 = "0002";