You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
396 lines
15 KiB
396 lines
15 KiB
|
1 month ago
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using UnityEngine;
|
||
|
|
|
||
|
|
public class BluetoothDeviceScript : MonoBehaviour
|
||
|
|
{
|
||
|
|
#if UNITY_IOS
|
||
|
|
public Dictionary<string, string> BLEStandardUUIDs = new Dictionary<string, string>();
|
||
|
|
#endif
|
||
|
|
|
||
|
|
public List<string> DiscoveredDeviceList;
|
||
|
|
|
||
|
|
public Action InitializedAction;
|
||
|
|
public Action DeinitializedAction;
|
||
|
|
public Action<string> ErrorAction;
|
||
|
|
public Action<string> ServiceAddedAction;
|
||
|
|
public Action StartedAdvertisingAction;
|
||
|
|
public Action StoppedAdvertisingAction;
|
||
|
|
public Action<string, string> DiscoveredPeripheralAction;
|
||
|
|
public Action<string, string, int, byte[]> DiscoveredPeripheralWithAdvertisingInfoAction;
|
||
|
|
public Action<BluetoothLEHardwareInterface.iBeaconData> DiscoveredBeaconAction;
|
||
|
|
public Action<string, string> RetrievedConnectedPeripheralAction;
|
||
|
|
public Action<string, byte[]> PeripheralReceivedWriteDataAction;
|
||
|
|
public Action<string> ConnectedPeripheralAction;
|
||
|
|
public Action<string> ConnectedDisconnectPeripheralAction;
|
||
|
|
public Action<string> DisconnectedPeripheralAction;
|
||
|
|
public Action<string, string> DiscoveredServiceAction;
|
||
|
|
public Action<string, string, string> DiscoveredCharacteristicAction;
|
||
|
|
public Action<string> DidWriteCharacteristicAction;
|
||
|
|
public Dictionary<string, Dictionary<string, Action<string>>> DidUpdateNotificationStateForCharacteristicAction;
|
||
|
|
public Dictionary<string, Dictionary<string, Action<string, string>>> DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction;
|
||
|
|
public Dictionary<string, Dictionary<string, Action<string, byte[]>>> DidUpdateCharacteristicValueAction;
|
||
|
|
public Dictionary<string, Dictionary<string, Action<string, string, byte[]>>> DidUpdateCharacteristicValueWithDeviceAddressAction;
|
||
|
|
public Action<string, int> RequestMtuAction;
|
||
|
|
public Action<string, int> ReadRSSIAction;
|
||
|
|
|
||
|
|
// Use this for initialization
|
||
|
|
void Start ()
|
||
|
|
{
|
||
|
|
DiscoveredDeviceList = new List<string> ();
|
||
|
|
DidUpdateNotificationStateForCharacteristicAction = new Dictionary<string, Dictionary<string, Action<string>>> ();
|
||
|
|
DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction = new Dictionary<string, Dictionary<string, Action<string, string>>> ();
|
||
|
|
DidUpdateCharacteristicValueAction = new Dictionary<string, Dictionary<string, Action<string, byte[]>>> ();
|
||
|
|
DidUpdateCharacteristicValueWithDeviceAddressAction = new Dictionary<string, Dictionary<string, Action<string, string, byte[]>>> ();
|
||
|
|
|
||
|
|
#if UNITY_IOS
|
||
|
|
BLEStandardUUIDs["Heart Rate Measurement"] = "00002A37-0000-1000-8000-00805F9B34FB";
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update is called once per frame
|
||
|
|
void Update ()
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
const string deviceInitializedString = "Initialized";
|
||
|
|
const string deviceDeInitializedString = "DeInitialized";
|
||
|
|
const string deviceErrorString = "Error";
|
||
|
|
const string deviceServiceAdded = "ServiceAdded";
|
||
|
|
const string deviceStartedAdvertising = "StartedAdvertising";
|
||
|
|
const string deviceStoppedAdvertising = "StoppedAdvertising";
|
||
|
|
const string deviceDiscoveredPeripheral = "DiscoveredPeripheral";
|
||
|
|
const string deviceDiscoveredBeacon = "DiscoveredBeacon";
|
||
|
|
const string deviceRetrievedConnectedPeripheral = "RetrievedConnectedPeripheral";
|
||
|
|
const string devicePeripheralReceivedWriteData = "PeripheralReceivedWriteData";
|
||
|
|
const string deviceConnectedPeripheral = "ConnectedPeripheral";
|
||
|
|
const string deviceDisconnectedPeripheral = "DisconnectedPeripheral";
|
||
|
|
const string deviceDiscoveredService = "DiscoveredService";
|
||
|
|
const string deviceDiscoveredCharacteristic = "DiscoveredCharacteristic";
|
||
|
|
const string deviceDidWriteCharacteristic = "DidWriteCharacteristic";
|
||
|
|
const string deviceDidUpdateNotificationStateForCharacteristic = "DidUpdateNotificationStateForCharacteristic";
|
||
|
|
const string deviceDidUpdateValueForCharacteristic = "DidUpdateValueForCharacteristic";
|
||
|
|
const string deviceLog = "Log";
|
||
|
|
const string deviceRequestMtu = "MtuChanged";
|
||
|
|
const string deviceReadRSSI = "DidReadRSSI";
|
||
|
|
|
||
|
|
public void OnBluetoothMessage (string message)
|
||
|
|
{
|
||
|
|
if (message != null)
|
||
|
|
{
|
||
|
|
char[] delim = new char[] { '~' };
|
||
|
|
string[] parts = message.Split (delim);
|
||
|
|
|
||
|
|
string log = "";
|
||
|
|
for (int i = 0; i < parts.Length; ++i)
|
||
|
|
log += string.Format("| {0}", parts[i]);
|
||
|
|
BluetoothLEHardwareInterface.Log(log);
|
||
|
|
|
||
|
|
if (message.Length >= deviceInitializedString.Length && message.Substring (0, deviceInitializedString.Length) == deviceInitializedString)
|
||
|
|
{
|
||
|
|
if (InitializedAction != null)
|
||
|
|
InitializedAction ();
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceLog.Length && message.Substring (0, deviceLog.Length) == deviceLog)
|
||
|
|
{
|
||
|
|
BluetoothLEHardwareInterface.Log (parts[1]);
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceDeInitializedString.Length && message.Substring (0, deviceDeInitializedString.Length) == deviceDeInitializedString)
|
||
|
|
{
|
||
|
|
BluetoothLEHardwareInterface.FinishDeInitialize ();
|
||
|
|
|
||
|
|
if (DeinitializedAction != null)
|
||
|
|
DeinitializedAction ();
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceErrorString.Length && message.Substring (0, deviceErrorString.Length) == deviceErrorString)
|
||
|
|
{
|
||
|
|
string error = "";
|
||
|
|
|
||
|
|
if (parts.Length >= 2)
|
||
|
|
error = parts[1];
|
||
|
|
|
||
|
|
if (ErrorAction != null)
|
||
|
|
ErrorAction (error);
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceServiceAdded.Length && message.Substring (0, deviceServiceAdded.Length) == deviceServiceAdded)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 2)
|
||
|
|
{
|
||
|
|
if (ServiceAddedAction != null)
|
||
|
|
ServiceAddedAction (parts[1]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceStartedAdvertising.Length && message.Substring (0, deviceStartedAdvertising.Length) == deviceStartedAdvertising)
|
||
|
|
{
|
||
|
|
BluetoothLEHardwareInterface.Log ("Started Advertising");
|
||
|
|
|
||
|
|
if (StartedAdvertisingAction != null)
|
||
|
|
StartedAdvertisingAction ();
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceStoppedAdvertising.Length && message.Substring (0, deviceStoppedAdvertising.Length) == deviceStoppedAdvertising)
|
||
|
|
{
|
||
|
|
BluetoothLEHardwareInterface.Log ("Stopped Advertising");
|
||
|
|
|
||
|
|
if (StoppedAdvertisingAction != null)
|
||
|
|
StoppedAdvertisingAction ();
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceDiscoveredPeripheral.Length && message.Substring (0, deviceDiscoveredPeripheral.Length) == deviceDiscoveredPeripheral)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 3)
|
||
|
|
{
|
||
|
|
// the first callback will only get called the first time this device is seen
|
||
|
|
// this is because it gets added to the a list in the DiscoveredDeviceList
|
||
|
|
// after that only the second callback will get called and only if there is
|
||
|
|
// advertising data available
|
||
|
|
if (!DiscoveredDeviceList.Contains (parts[1] + "|" + parts[2]))
|
||
|
|
{
|
||
|
|
DiscoveredDeviceList.Add (parts[1] + "|" + parts[2]);
|
||
|
|
|
||
|
|
if (DiscoveredPeripheralAction != null)
|
||
|
|
DiscoveredPeripheralAction (parts[1], parts[2]);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (parts.Length >= 5 && DiscoveredPeripheralWithAdvertisingInfoAction != null)
|
||
|
|
{
|
||
|
|
// get the rssi from the 4th value
|
||
|
|
int rssi = 0;
|
||
|
|
if (!int.TryParse (parts[3], out rssi))
|
||
|
|
rssi = 0;
|
||
|
|
|
||
|
|
// parse the base 64 encoded data that is the 5th value
|
||
|
|
byte[] bytes = System.Convert.FromBase64String (parts[4]);
|
||
|
|
|
||
|
|
DiscoveredPeripheralWithAdvertisingInfoAction (parts[1], parts[2], rssi, bytes);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceDiscoveredBeacon.Length && message.Substring (0, deviceDiscoveredBeacon.Length) == deviceDiscoveredBeacon)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 7)
|
||
|
|
{
|
||
|
|
var iBeaconData = new BluetoothLEHardwareInterface.iBeaconData ();
|
||
|
|
|
||
|
|
iBeaconData.UUID = parts[1];
|
||
|
|
if (!int.TryParse (parts[2], out iBeaconData.Major))
|
||
|
|
iBeaconData.Major = 0;
|
||
|
|
if (!int.TryParse (parts[3], out iBeaconData.Minor))
|
||
|
|
iBeaconData.Minor = 0;
|
||
|
|
if (!int.TryParse (parts[4], out iBeaconData.RSSI))
|
||
|
|
iBeaconData.RSSI = 0;
|
||
|
|
if (!int.TryParse (parts[5], out iBeaconData.AndroidSignalPower))
|
||
|
|
iBeaconData.AndroidSignalPower = 0;
|
||
|
|
int iOSProximity = 0;
|
||
|
|
if (!int.TryParse (parts[6], out iOSProximity))
|
||
|
|
iOSProximity = 0;
|
||
|
|
iBeaconData.iOSProximity = (BluetoothLEHardwareInterface.iOSProximity)iOSProximity;
|
||
|
|
|
||
|
|
if (DiscoveredBeaconAction != null)
|
||
|
|
DiscoveredBeaconAction (iBeaconData);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceRetrievedConnectedPeripheral.Length && message.Substring (0, deviceRetrievedConnectedPeripheral.Length) == deviceRetrievedConnectedPeripheral)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 3)
|
||
|
|
{
|
||
|
|
DiscoveredDeviceList.Add (parts[1]);
|
||
|
|
|
||
|
|
if (RetrievedConnectedPeripheralAction != null)
|
||
|
|
RetrievedConnectedPeripheralAction (parts[1], parts[2]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (message.Length >= devicePeripheralReceivedWriteData.Length && message.Substring (0, devicePeripheralReceivedWriteData.Length) == devicePeripheralReceivedWriteData)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 3)
|
||
|
|
OnPeripheralData (parts[1], parts[2]);
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceConnectedPeripheral.Length && message.Substring (0, deviceConnectedPeripheral.Length) == deviceConnectedPeripheral)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 2 && ConnectedPeripheralAction != null)
|
||
|
|
ConnectedPeripheralAction (parts[1]);
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceDisconnectedPeripheral.Length && message.Substring (0, deviceDisconnectedPeripheral.Length) == deviceDisconnectedPeripheral)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 2)
|
||
|
|
{
|
||
|
|
if (ConnectedDisconnectPeripheralAction != null)
|
||
|
|
ConnectedDisconnectPeripheralAction (parts[1]);
|
||
|
|
|
||
|
|
if (DisconnectedPeripheralAction != null)
|
||
|
|
DisconnectedPeripheralAction (parts[1]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceDiscoveredService.Length && message.Substring (0, deviceDiscoveredService.Length) == deviceDiscoveredService)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 3 && DiscoveredServiceAction != null)
|
||
|
|
DiscoveredServiceAction (parts[1], parts[2]);
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceDiscoveredCharacteristic.Length && message.Substring (0, deviceDiscoveredCharacteristic.Length) == deviceDiscoveredCharacteristic)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 4 && DiscoveredCharacteristicAction != null)
|
||
|
|
DiscoveredCharacteristicAction (parts[1], parts[2], parts[3]);
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceDidWriteCharacteristic.Length && message.Substring (0, deviceDidWriteCharacteristic.Length) == deviceDidWriteCharacteristic)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 2 && DidWriteCharacteristicAction != null)
|
||
|
|
DidWriteCharacteristicAction (parts[1]);
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceDidUpdateNotificationStateForCharacteristic.Length && message.Substring (0, deviceDidUpdateNotificationStateForCharacteristic.Length) == deviceDidUpdateNotificationStateForCharacteristic)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 3)
|
||
|
|
{
|
||
|
|
if (DidUpdateNotificationStateForCharacteristicAction != null && DidUpdateNotificationStateForCharacteristicAction.ContainsKey (parts[1]))
|
||
|
|
{
|
||
|
|
var characteristicAction = DidUpdateNotificationStateForCharacteristicAction[parts[1]];
|
||
|
|
if (characteristicAction != null && characteristicAction.ContainsKey (parts[2]))
|
||
|
|
{
|
||
|
|
var action = characteristicAction[parts[2]];
|
||
|
|
if (action != null)
|
||
|
|
action (parts[2]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction != null && DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction.ContainsKey (parts[1]))
|
||
|
|
{
|
||
|
|
var characteristicAction = DidUpdateNotificationStateForCharacteristicWithDeviceAddressAction[parts[1]];
|
||
|
|
if (characteristicAction != null && characteristicAction.ContainsKey (parts[2]))
|
||
|
|
{
|
||
|
|
var action = characteristicAction[parts[2]];
|
||
|
|
if (action != null)
|
||
|
|
action (parts[1], parts[2]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceDidUpdateValueForCharacteristic.Length && message.Substring (0, deviceDidUpdateValueForCharacteristic.Length) == deviceDidUpdateValueForCharacteristic)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 4)
|
||
|
|
OnBluetoothData (parts[1], parts[2], parts[3]);
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceRequestMtu.Length && message.Substring(0, deviceRequestMtu.Length) == deviceRequestMtu)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 3)
|
||
|
|
{
|
||
|
|
if (RequestMtuAction != null)
|
||
|
|
{
|
||
|
|
int mtu = 0;
|
||
|
|
if (int.TryParse(parts[2], out mtu))
|
||
|
|
RequestMtuAction(parts[1], mtu);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (message.Length >= deviceReadRSSI.Length && message.Substring(0, deviceReadRSSI.Length) == deviceReadRSSI)
|
||
|
|
{
|
||
|
|
if (parts.Length >= 3)
|
||
|
|
{
|
||
|
|
if (ReadRSSIAction != null)
|
||
|
|
{
|
||
|
|
int rssi = 0;
|
||
|
|
if (int.TryParse(parts[2], out rssi))
|
||
|
|
ReadRSSIAction(parts[1], rssi);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void OnBluetoothData (string base64Data)
|
||
|
|
{
|
||
|
|
OnBluetoothData ("", "", base64Data);
|
||
|
|
}
|
||
|
|
|
||
|
|
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))
|
||
|
|
{
|
||
|
|
var characteristicAction = DidUpdateCharacteristicValueAction[deviceAddress];
|
||
|
|
#if UNITY_ANDROID
|
||
|
|
characteristic = characteristic.ToLower ();
|
||
|
|
#endif
|
||
|
|
if (characteristicAction != null && characteristicAction.ContainsKey (characteristic))
|
||
|
|
{
|
||
|
|
var action = characteristicAction[characteristic];
|
||
|
|
if (action != null)
|
||
|
|
action (characteristic, bytes);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (DidUpdateCharacteristicValueWithDeviceAddressAction != null && DidUpdateCharacteristicValueWithDeviceAddressAction.ContainsKey (deviceAddress))
|
||
|
|
{
|
||
|
|
var characteristicAction = DidUpdateCharacteristicValueWithDeviceAddressAction[deviceAddress];
|
||
|
|
#if UNITY_ANDROID
|
||
|
|
characteristic = characteristic.ToLower ();
|
||
|
|
#endif
|
||
|
|
if (characteristicAction != null && characteristicAction.ContainsKey (characteristic))
|
||
|
|
{
|
||
|
|
var action = characteristicAction[characteristic];
|
||
|
|
if (action != null)
|
||
|
|
action (deviceAddress, characteristic, bytes);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public void OnPeripheralData (string characteristic, string base64Data)
|
||
|
|
{
|
||
|
|
if (base64Data != null)
|
||
|
|
{
|
||
|
|
byte[] bytes = System.Convert.FromBase64String (base64Data);
|
||
|
|
if (bytes.Length > 0)
|
||
|
|
{
|
||
|
|
BluetoothLEHardwareInterface.Log ("Peripheral Received: " + characteristic);
|
||
|
|
|
||
|
|
string byteString = "";
|
||
|
|
foreach (byte b in bytes)
|
||
|
|
byteString += string.Format ("{0:X2}", b);
|
||
|
|
|
||
|
|
BluetoothLEHardwareInterface.Log (byteString);
|
||
|
|
|
||
|
|
if (PeripheralReceivedWriteDataAction != null)
|
||
|
|
PeripheralReceivedWriteDataAction (characteristic, bytes);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#if UNITY_IOS
|
||
|
|
private void IncludeCoreLocationFramework()
|
||
|
|
{
|
||
|
|
// this method is here because Unity now only includes CoreLocation
|
||
|
|
// if there are methods in the .cs code that access it
|
||
|
|
Input.location.Stop ();
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
public void OnApplicationQuit()
|
||
|
|
{
|
||
|
|
if (Application.isEditor)
|
||
|
|
{
|
||
|
|
BluetoothLEHardwareInterface.DeInitialize(() =>
|
||
|
|
{
|
||
|
|
BluetoothLEHardwareInterface.Log("Deinitialize complete");
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|