I am building a VR cycling application in Unity. In my project I need to be able to connect the exercise bike and the heartrate monitor via Bluetooth concurrently, however I am experiencing a weird problem when I do that.
Both devices work fine when connected on their own, however the received data goes all wrong when I connect them both at the same time.
When the exercise bike is receiving data on its own it receives Speed, Cadence, Power. However when I connect the heartrate monitor at the same time it receives Speed, Distance, and Expended Energy.
I have attached my relevant code scripts, any help would be greatly appreciated I am genuinely clueless as to why this is happening as I have little experience with Bluetooth.
Scripts:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using UnityEngine;
public class BleApi
{
// dll calls
public enum ScanStatus { PROCESSING, AVAILABLE, FINISHED };
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DeviceUpdate
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string id;
[MarshalAs(UnmanagedType.I1)]
public bool isConnectable;
[MarshalAs(UnmanagedType.I1)]
public bool isConnectableUpdated;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string name;
[MarshalAs(UnmanagedType.I1)]
public bool nameUpdated;
}
[DllImport("BleWinrtDll.dll", EntryPoint = "StartDeviceScan")]
public static extern void StartDeviceScan();
[DllImport("BleWinrtDll.dll", EntryPoint = "PollDevice")]
public static extern ScanStatus PollDevice(ref DeviceUpdate device, bool block);
[DllImport("BleWinrtDll.dll", EntryPoint = "StopDeviceScan")]
public static extern void StopDeviceScan();
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Service
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string uuid;
};
[DllImport("BleWinrtDll.dll", EntryPoint = "ScanServices", CharSet = CharSet.Unicode)]
public static extern void ScanServices(string deviceId);
[DllImport("BleWinrtDll.dll", EntryPoint = "PollService")]
public static extern ScanStatus PollService(out Service service, bool block);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct Characteristic
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string uuid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
public string userDescription;
};
[DllImport("BleWinrtDll.dll", EntryPoint = "ScanCharacteristics", CharSet = CharSet.Unicode)]
public static extern void ScanCharacteristics(string deviceId, string serviceId);
[DllImport("BleWinrtDll.dll", EntryPoint = "PollCharacteristic")]
public static extern ScanStatus PollCharacteristic(out Characteristic characteristic, bool block);
[DllImport("BleWinrtDll.dll", EntryPoint = "SubscribeCharacteristic_Read", CharSet = CharSet.Unicode)]
public static extern bool SubscribeCharacteristic_Read(string deviceId, string serviceId, string characteristicId, bool block);
[DllImport("BleWinrtDll.dll", EntryPoint = "SubscribeCharacteristic_Write", CharSet = CharSet.Unicode)]
public static extern bool SubscribeCharacteristic_Write(string deviceId, string serviceId, string characteristicId, bool block);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct BLEData
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
public byte[] buf;
[MarshalAs(UnmanagedType.I2)]
public short size;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string deviceId;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string serviceUuid;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string characteristicUuid;
};
[DllImport("BleWinrtDll.dll", EntryPoint = "PollData")]
public static extern bool PollData(out BLEData data, bool block);
[DllImport("BleWinrtDll.dll", EntryPoint = "SendData")]
public static extern bool SendData(in BLEData data, bool block);
[DllImport("BleWinrtDll.dll", EntryPoint = "Quit")]
public static extern void Quit();
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct ErrorMessage
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
public string msg;
};
[DllImport("BleWinrtDll.dll", EntryPoint = "GetError")]
public static extern void GetError(out ErrorMessage buf);
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FTMS_IndoorBike
{
string device_name;
string service_id;
string read_characteristic;
string write_characteristic;
public bool want_connect = true;
Dictionary<string, Dictionary<string, string>> devices = new Dictionary<string, Dictionary<string, string>>();
string selectedDeviceId = "";
string selectedServiceId = "";
string selectedCharacteristicId = "";
bool isSubscribed = false;
public string output;
public float speed; public bool has_speed = false;
public float average_speed; public bool has_average_speed = false;
public float rpm; public bool has_rpm = false;
public float average_rpm; public bool has_average_rpm = false;
public float distance; public bool has_distance = false;
public float resistance; public bool has_resistance = false;
public float power; public bool has_power = false;
public float average_power; public bool has_average_power = false;
public float expended_energy; public bool has_expended_energy = false;
string lastError;
float last_write_time = 0.0f;
int sended_resistance = 0;
MonoBehaviour mono;
FTMS_UI ui;
public FTMS_IndoorBike(MonoBehaviour _mono, FTMS_UI _ui)
{
mono = _mono;
ui = _ui;
}
// Start is called before the first frame update
public IEnumerator connect(string _device_name = "WattbikePT28004316", string _service_id = "{babf1723-cedb-444c-88c3-c672c7a59806}", string _read_characteristic = "{babf1724-cedb-444c-88c3-c672c7a59806}", string _write_characteristic = "{babf1725-cedb-444c-88c3-c672c7a59806}")
{
if (!want_connect) yield break;
device_name = _device_name;
service_id = _service_id;
read_characteristic = _read_characteristic;
write_characteristic = _write_characteristic;
quit();
yield return mono.StartCoroutine(connect_device());
if (selectedDeviceId.Length == 0) yield break;
Debug.Log("connecting device finish");
yield return mono.StartCoroutine(connect_service());
if (selectedServiceId.Length == 0) yield break;
Debug.Log("connecting service finish");
yield return mono.StartCoroutine(connect_read_characteristic());
if (selectedCharacteristicId.Length == 0) yield break;
Debug.Log("connecting read characteristic finish");
read_subscribe();
ui.connected = true;
}
IEnumerator connect_device()
{
Debug.Log("connecting device...");
BleApi.StartDeviceScan();
BleApi.ScanStatus status = BleApi.ScanStatus.AVAILABLE;
BleApi.DeviceUpdate device_res = new BleApi.DeviceUpdate();
do
{
status = BleApi.PollDevice(ref device_res, false);
//Debug.Log(count++);
if (status == BleApi.ScanStatus.AVAILABLE)
{
if (!devices.ContainsKey(device_res.id))
devices[device_res.id] = new Dictionary<string, string>() {
{ "name", "" },
{ "isConnectable", "False" }
};
if (device_res.nameUpdated)
devices[device_res.id]["name"] = device_res.name;
if (device_res.isConnectableUpdated)
devices[device_res.id]["isConnectable"] = device_res.isConnectable.ToString();
// consider only devices which have a name and which are connectable
if (devices[device_res.id]["name"] == device_name && devices[device_res.id]["isConnectable"] == "True")
{
//BleApi.Connect(device_res.id);
selectedDeviceId = device_res.id;
break;
}
}
else if (status == BleApi.ScanStatus.FINISHED)
{
if (selectedDeviceId.Length == 0)
{
Debug.LogError("device " + device_name + " not found!");
}
}
yield return 0;
} while (status == BleApi.ScanStatus.AVAILABLE || status == BleApi.ScanStatus.PROCESSING);
}
IEnumerator connect_service()
{
Debug.Log("connecting service...");
BleApi.ScanServices(selectedDeviceId);
BleApi.ScanStatus status;
BleApi.Service service_res = new BleApi.Service();
do
{
status = BleApi.PollService(out service_res, false);
if (status == BleApi.ScanStatus.AVAILABLE)
{
if (service_res.uuid == service_id)
{
selectedServiceId = service_res.uuid;
break;
}
}
else if (status == BleApi.ScanStatus.FINISHED)
{
if (selectedServiceId.Length == 0)
{
Debug.LogError("service " + service_id + " not found!");
}
}
yield return 0;
} while (status == BleApi.ScanStatus.AVAILABLE || status == BleApi.ScanStatus.PROCESSING);
}
IEnumerator connect_read_characteristic()
{
Debug.Log("connecting characteristic...");
BleApi.ScanCharacteristics(selectedDeviceId, selectedServiceId);
BleApi.ScanStatus status;
BleApi.Characteristic characteristics_res = new BleApi.Characteristic();
do
{
status = BleApi.PollCharacteristic(out characteristics_res, false);
if (status == BleApi.ScanStatus.AVAILABLE)
{
if (characteristics_res.uuid == read_characteristic)
{
selectedCharacteristicId = characteristics_res.uuid;
break;
}
}
else if (status == BleApi.ScanStatus.FINISHED)
{
if (selectedCharacteristicId.Length == 0)
{
Debug.LogError("characteristic " + read_characteristic + " not found!");
}
}
yield return 0;
} while (status == BleApi.ScanStatus.AVAILABLE || status == BleApi.ScanStatus.PROCESSING);
}
void read_subscribe()
{
Debug.Log("Subscribe...");
BleApi.SubscribeCharacteristic_Read(selectedDeviceId, selectedServiceId, selectedCharacteristicId, false);
isSubscribed = true;
}
public void quit()
{
BleApi.Quit();
}
// Update is called once per frame
public void Update()
{
if (isSubscribed)
{
BleApi.BLEData res = new BleApi.BLEData();
while (BleApi.PollData(out res, false))
{
{
has_speed = false;
has_average_speed = false;
has_rpm = false;
has_average_rpm = false;
has_distance = false;
has_resistance = false;
has_power = false;
has_average_power = false;
has_expended_energy = false;
}
output = String.Empty;
int index = 0;
int flags = BitConverter.ToUInt16(res.buf, index);
index += 2;
if ((flags & 0) == 0)
{
has_speed = true;
float value = (float)BitConverter.ToUInt16(res.buf, index);
speed = (value * 1.0f) / 100.0f;
output += "Speed: " + speed + "\n";
index += 2;
}
if ((flags & 2) > 0)
{
//??
has_average_speed = true;
average_speed = BitConverter.ToUInt16(res.buf, index);
output += "Average Speed: " + average_speed + "\n";
index += 2;
}
if ((flags & 4) > 0)
{
rpm = (BitConverter.ToUInt16(res.buf, index) * 1.0f) / 2.0f;
output += "RPM: (rev/min): " + rpm + "\n";
index += 2;
}
if ((flags & 8) > 0)
{
average_rpm = (BitConverter.ToUInt16(res.buf, index) * 1.0f) / 2.0f;
output += "Average RPM: " + average_rpm + "\n";
index += 2;
}
if ((flags & 16) > 0)
{
distance = BitConverter.ToUInt16(res.buf, index); // ?????s
output += "Distance (meter): " + distance + "\n";
index += 2;
}
if ((flags & 32) > 0)
{
resistance = BitConverter.ToInt16(res.buf, index);
output += "Resistance: " + resistance + "\n";
index += 2;
}
if ((flags & 64) > 0)
{
power = BitConverter.ToInt16(res.buf, index);
output += "Power (Watt): " + power + "\n";
index += 2;
}
if ((flags & 128) > 0)
{
average_power = BitConverter.ToInt16(res.buf, index);
output += "AveragePower: " + average_power + "\n";
index += 2;
}
if ((flags & 256) > 0)
{
expended_energy = BitConverter.ToUInt16(res.buf, index);
output += "ExpendedEnergy: " + expended_energy + "\n";
index += 2;
}
//output += "Resistance: " + sended_resistance + "\n";
}
// log potential errors
BleApi.ErrorMessage res_err = new BleApi.ErrorMessage();
BleApi.GetError(out res_err);
if (lastError != res_err.msg)
{
Debug.LogError(res_err.msg);
lastError = res_err.msg;
}
}
}
private byte[] Convert16(string strText)
{
strText = strText.Replace(" ", "");
byte[] bText = new byte[strText.Length / 2];
for (int i = 0; i < strText.Length / 2; i++)
{
bText[i] = Convert.ToByte(Convert.ToInt32(strText.Substring(i * 2, 2), 16));
}
return bText;
}
public void Write(string msg)
{
byte[] payload22 = Convert16(msg);
BleApi.BLEData data = new BleApi.BLEData();
data.buf = new byte[512];
data.size = (short)payload22.Length;
data.deviceId = selectedDeviceId;
data.serviceUuid = selectedServiceId;
data.characteristicUuid = write_characteristic;
for (int i = 0; i < payload22.Length; i++)
{
data.buf[i] = payload22[i];
}
BleApi.SendData(in data, false);
}
public void write_resistance(float val)
{
write_resistance(Mathf.FloorToInt(val));
}
public void write_resistance(int val)
{
if (Time.time - last_write_time < 0.1f)
{
return;
}
else {
last_write_time = Time.time;
}
Debug.Log("write resistance: " + val);
BleApi.SubscribeCharacteristic_Write(selectedDeviceId, selectedServiceId, write_characteristic, false);
Write("00");
byte resistance1 = Convert.ToByte(val % 256);
byte resistance2 = Convert.ToByte(val / 256);
byte[] payload = { 0x11, 0x00, 0x00, resistance1, resistance2, 0x00, 0x00 };
BleApi.BLEData data = new BleApi.BLEData();
data.buf = new byte[512];
data.deviceId = selectedDeviceId;
data.serviceUuid = selectedServiceId;
data.characteristicUuid = write_characteristic;
for (int i = 0; i < payload.Length; i++){
data.buf[i] = payload[i];
}
data.size = (short)payload.Length;
BleApi.SendData(in data, false);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class FTMS_UI : MonoBehaviour
{
// Start is called before the first frame update
public bool connected = false;
public FTMS_IndoorBike connector;
public string infoText;
public Text resistance_show;
[SerializeField] private DataManager dataManager;
public string device_name = "WattbikePT28004316";
public string service_id = "{00001826-0000-1000-8000-00805f9b34fb}";
public string read_characteristic = "{00002ad2-0000-1000-8000-00805f9b34fb}";
public string write_characteristic= "{00002ad9-0000-1000-8000-00805f9b34fb}";
public void BeginConnection()
{
connector = new FTMS_IndoorBike(this, this);
connect();
}
public void connect() {
if (device_name.Length > 0 && service_id.Length > 0 && read_characteristic.Length > 0 && write_characteristic.Length > 0)
{
StartCoroutine(connector.connect(device_name, service_id, read_characteristic, write_characteristic));
}
}
public void write_resistance(float val) {
if (connected)
{
connector.write_resistance(val);
resistance_show.text = "Resistance: " + Mathf.FloorToInt(val).ToString();
}
}
// Update is called once per frame
void Update()
{
if (connected)
{
connector.Update();
infoText = connector.output;
}
}
private void OnApplicationQuit()
{
connector.quit();
}
public void change_device_name(string _device_name) {
device_name = _device_name;
}
public void change_service_id(string _service_id)
{
service_id = _service_id;
}
public void change_read_characteristic(string _read_characteristic)
{
read_characteristic = _read_characteristic;
}
public void change_write_characteristic(string _write_characteristic)
{
write_characteristic = _write_characteristic;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class HeartRateMonitor
{
string device_name;
string service_id;
string read_characteristic;
public bool want_connect = true;
Dictionary<string, Dictionary<string, string>> devices = new Dictionary<string, Dictionary<string, string>>();
string selectedDeviceId = "";
string selectedServiceId = "";
string selectedCharacteristicId = "";
bool isSubscribed = false;
public string output;
public int heartRate; public bool has_heartRate = false;
public bool contact_detected; public bool has_contact = false;
public bool rr_interval_present; public bool has_rr_interval = false;
public List<int> rr_intervals = new List<int>(); // RR intervals in ms
string lastError;
MonoBehaviour mono;
HeartRateMonitor_UI ui;
public HeartRateMonitor(MonoBehaviour _mono, HeartRateMonitor_UI _ui)
{
mono = _mono;
ui = _ui;
}
// Start is called before the first frame update
public IEnumerator connect(string _device_name = "Polar H10 D7E96D26", string _service_id = "{0000180d-0000-1000-8000-00805f9b34fb}", string _read_characteristic = "{00002A37-0000-1000-8000-00805f9b34fb}")
{
if (!want_connect) yield break;
device_name = _device_name;
service_id = _service_id;
read_characteristic = _read_characteristic;
quit();
yield return mono.StartCoroutine(connect_device());
if (selectedDeviceId.Length == 0) yield break;
Debug.Log("connecting device finish");
yield return mono.StartCoroutine(connect_service());
if (selectedServiceId.Length == 0) yield break;
Debug.Log("connecting service finish");
yield return mono.StartCoroutine(connect_read_characteristic());
if (selectedCharacteristicId.Length == 0) yield break;
Debug.Log("connecting read characteristic finish");
read_subscribe();
ui.connected = true;
}
IEnumerator connect_device()
{
Debug.Log("connecting device...");
BleApi.StartDeviceScan();
BleApi.ScanStatus status = BleApi.ScanStatus.AVAILABLE;
BleApi.DeviceUpdate device_res = new BleApi.DeviceUpdate();
do
{
status = BleApi.PollDevice(ref device_res, false);
if (status == BleApi.ScanStatus.AVAILABLE)
{
if (!devices.ContainsKey(device_res.id))
devices[device_res.id] = new Dictionary<string, string>() {
{ "name", "" },
{ "isConnectable", "False" }
};
if (device_res.nameUpdated)
devices[device_res.id]["name"] = device_res.name;
if (device_res.isConnectableUpdated)
devices[device_res.id]["isConnectable"] = device_res.isConnectable.ToString();
// consider only devices which have a name and which are connectable
if (devices[device_res.id]["name"] == device_name && devices[device_res.id]["isConnectable"] == "True")
{
selectedDeviceId = device_res.id;
break;
}
}
else if (status == BleApi.ScanStatus.FINISHED)
{
if (selectedDeviceId.Length == 0)
{
Debug.LogError("device " + device_name + " not found!");
}
}
yield return 0;
} while (status == BleApi.ScanStatus.AVAILABLE || status == BleApi.ScanStatus.PROCESSING);
}
IEnumerator connect_service()
{
Debug.Log("connecting service...");
BleApi.ScanServices(selectedDeviceId);
BleApi.ScanStatus status;
BleApi.Service service_res = new BleApi.Service();
do
{
status = BleApi.PollService(out service_res, false);
if (status == BleApi.ScanStatus.AVAILABLE)
{
if (service_res.uuid == service_id)
{
selectedServiceId = service_res.uuid;
break;
}
}
else if (status == BleApi.ScanStatus.FINISHED)
{
if (selectedServiceId.Length == 0)
{
Debug.LogError("service " + service_id + " not found!");
}
}
yield return 0;
} while (status == BleApi.ScanStatus.AVAILABLE || status == BleApi.ScanStatus.PROCESSING);
}
IEnumerator connect_read_characteristic()
{
Debug.Log("connecting characteristic...");
BleApi.ScanCharacteristics(selectedDeviceId, selectedServiceId);
BleApi.ScanStatus status;
BleApi.Characteristic characteristics_res = new BleApi.Characteristic();
do
{
status = BleApi.PollCharacteristic(out characteristics_res, false);
if (status == BleApi.ScanStatus.AVAILABLE)
{
if (characteristics_res.uuid == read_characteristic)
{
selectedCharacteristicId = characteristics_res.uuid;
break;
}
}
else if (status == BleApi.ScanStatus.FINISHED)
{
if (selectedCharacteristicId.Length == 0)
{
Debug.LogError("characteristic " + read_characteristic + " not found!");
}
}
yield return 0;
} while (status == BleApi.ScanStatus.AVAILABLE || status == BleApi.ScanStatus.PROCESSING);
}
void read_subscribe()
{
Debug.Log("Subscribe...");
BleApi.SubscribeCharacteristic_Read(selectedDeviceId, selectedServiceId, selectedCharacteristicId, false);
isSubscribed = true;
}
public void quit()
{
BleApi.Quit();
}
// Update is called once per frame
public void Update()
{
if (isSubscribed)
{
BleApi.BLEData res = new BleApi.BLEData();
while (BleApi.PollData(out res, false))
{
has_heartRate = false;
has_contact = false;
has_rr_interval = false;
rr_intervals.Clear();
output = String.Empty;
int index = 0;
// Read first byte for flags
byte flags = res.buf[index++];
// Heart Rate Value Format bit (0 = UINT8, 1 = UINT16)
bool isUint16 = (flags & 0x01) != 0;
// Sensor Contact Status bits
bool contactSensorPresent = (flags & 0x04) != 0;
if (contactSensorPresent)
{
has_contact = true;
contact_detected = (flags & 0x02) != 0;
output += "Contact: " + (contact_detected ? "Detected" : "Not Detected") + "\n";
}
// Energy Expended Status bit
bool energyExpendedPresent = (flags & 0x08) != 0;
// RR-Interval bit
rr_interval_present = (flags & 0x10) != 0;
has_rr_interval = rr_interval_present;
// Read the Heart Rate Measurement Value
if (isUint16)
{
heartRate = BitConverter.ToUInt16(res.buf, index);
index += 2;
}
else
{
heartRate = res.buf[index++];
}
has_heartRate = true;
output += "Heart Rate: " + heartRate + " bpm\n";
// Skip Energy Expended field if present
if (energyExpendedPresent)
{
index += 2; // UINT16
}
// Read RR-Intervals if present
if (rr_interval_present)
{
// Calculate how many RR intervals are in the packet
// Each RR interval is 2 bytes (UINT16)
int remainingBytes = res.size - index;
int rrIntervalCount = remainingBytes / 2;
output += "RR Intervals: ";
for (int i = 0; i < rrIntervalCount; i++)
{
int rrInterval = BitConverter.ToUInt16(res.buf, index);
index += 2;
// RR intervals are in 1/1024 second units, convert to milliseconds
int rrIntervalMs = (int)Math.Round(rrInterval * 1000.0 / 1024.0);
rr_intervals.Add(rrIntervalMs);
output += rrIntervalMs + "ms ";
}
output += "\n";
}
}
// log potential errors
BleApi.ErrorMessage res_err = new BleApi.ErrorMessage();
BleApi.GetError(out res_err);
if (lastError != res_err.msg && !string.IsNullOrEmpty(res_err.msg))
{
Debug.LogError(res_err.msg);
lastError = res_err.msg;
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HeartRateMonitor_UI : MonoBehaviour
{
public bool connected = false;
public HeartRateMonitor connector;
[SerializeField] private DataManager dataManager;
[SerializeField] private string infoText;
// Default values for heart rate monitor - can be changed in inspector
public string device_name = "Polar H10 D7E96D26";
public string service_id = "{0000180d-0000-1000-8000-00805f9b34fb}"; // Standard Heart Rate Service UUID
public string read_characteristic = "{00002a37-0000-1000-8000-00805f9b34fb}"; // Heart Rate Measurement characteristic
private void Start()
{
dataManager = FindObjectOfType<DataManager>();
}
public void BeginConnection()
{
connector = new HeartRateMonitor(this, this);
connect();
}
public void connect()
{
if (device_name.Length > 0 && service_id.Length > 0 && read_characteristic.Length > 0)
{
StartCoroutine(connector.connect(device_name, service_id, read_characteristic));
}
}
// Update is called once per frame
void Update()
{
if (connected)
{
connector.Update();
infoText = connector.output;
if (connector.has_heartRate)
{
}
}
}
private void OnApplicationQuit()
{
connector.quit();
}
// Methods to update device parameters from UI
public void change_device_name(string _device_name)
{
device_name = _device_name;
}
public void change_service_id(string _service_id)
{
service_id = _service_id;
}
public void change_read_characteristic(string _read_characteristic)
{
read_characteristic = _read_characteristic;
}
// Method to reconnect with new parameters
public void reconnect()
{
connected = false;
connector.quit();
connect();
}
}