Reputation: 181
I'm stuck with this task about reading some data from my BLE Device.
I have a HM-19 DSD Tech Bluetooth LE module on my target machine, and I want communicate with it with my smartphone.
I'm using Xamarin with Plugin.BLE to try to achieve this.
There is my BluetoothPage.xaml.cs
code
public partial class BluetoothPage : ContentPage
{
IAdapter adapter;
ObservableCollection<IDevice> devicesList;
ListView pageList;
public BluetoothPage()
{
adapter = CrossBluetoothLE.Current.Adapter;
deviceList = new ObservableCollection<IDevice>();
pageList = new ListView();
pageList.ItemSource = deviceList;
pageList.ItemSelected += ActionConnect;
pageStackLayout.Children.Add(pageList); // Layout component declared on BluetoothPage.xaml
}
private void ButtonScan(object sender, EventArgs e)
{
try
{
devicesList.Clear();
adapter.DeviceDiscovered += (s, a) =>
{
devicesList.Add(a.Device);
};
if(!adapter.isScanning)
{
await adapter.StartScanningForDevicesAsync();
}
}
catch(Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
}
private async void ActionConnect(object sender, SelectedItemChangedEventArgs se)
{
if(se != null)
{
try
{
if(adapter.IsScanning)
{
await adapter.StopScanningForDevicesAsync();
}
await adapter.ConnectToDeviceAsync((IDevice)pageList.SelectedItem);
IDevice device = adapter.ConnectedDevices[0];
// now get the service and characteristics of connected device
var services = await device.GetServicesAsync();
IService service = services[0];
var characteristics = await service.GetCharacteristicsAsync();
ICharacteristic characteristic = characteristics[0];
// now we can write and hopefully read values
byte[] data = { Coderequest.InitRequest, Coderequest.Info }; // My message to sendo to the machine to trigger his functions and his response
byte[] response = { 0 };
await characteristic.WriteAsync(data); // Send the data
response = await characteristic.ReadAsync() // Theorically we reading the response from the machine by BLE
}
catch(Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
}
}
}
When I launch my app:
But I didn't get the response expected, I'm reading always the same bytes (that are maybe default values) that aren't the bytes I expected.
The strange thing is that if I use the DSD Tech Demo App for the HM-19 module and execute same instructions (connect, send and read) it works! I send the data that trigger a response by machine and the demo app show the expect right bytes I sent from the machine.
So...how I can read that data? On the developer site the docs barely guide you on scan and connect, but on the write/read lacks of info. It is very disappointing and this plugin is the best on the Nuget repos.
Can anyone help me, where I'm doing bad?
This is a link for the plugin, maybe I missed something https://github.com/xabre/xamarin-bluetooth-le
Upvotes: 4
Views: 2924
Reputation: 1
The program is not working well, so I looked it up and found something similar, What is missing is not finding the Bluetooth device. I'm sorry, but if you can share, please send the entire program by e-mail. thank you kdg000@empas.com
MainPage.xaml
<Button Text="Search" Clicked="searchDevice"/>
<ListView x:Name="DevicesList"
CachingStrategy="RecycleElement"
ItemSelected="DevicesList_OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding name}"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MainPage.xaml.cs
namespace employeeID { public partial class MainPage : ContentPage { IAdapter adapter; IBluetoothLE ble; ObservableCollection devicelist; IDevice device;
public MainPage()
{
InitializeComponent();
ble = CrossBluetoothLE.Current;
adapter = CrossBluetoothLE.Current.Adapter;
devicelist = new ObservableCollection<IDevice>();
DevicesList.ItemsSource = devicelist;
}
private async void searchDevice(object sender, EventArgs e)
{
if (ble.State == BluetoothState.Off)
{
await DisplayAlert("Message", "Bluetooth is not available.", "OK");
}
else
{
try
{
devicelist.Clear();
adapter.ScanTimeout = 10000;
adapter.ScanMode = ScanMode.Balanced;
adapter.DeviceDiscovered += (s, a) =>
{
//devicelist.Add(new ScanResultViewModel() { Device = a.Device, IsConnect = "Status: " + a.Device.State.ToString(), Uuid = "UUID:" + a.Device.Id.ToString() });
devicelist.Add(a.Device);
};
//We have to test if the device is scanning
if (!ble.Adapter.IsScanning)
{
await adapter.StartScanningForDevicesAsync();
}
}
catch (Exception ex)
{
await DisplayAlert("Notice", ex.Message.ToString(), "Error !");
}
}
}
private async void DevicesList_OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
device = DevicesList.SelectedItem as IDevice;
var result = await DisplayAlert("Message", "Do you want to connect to this device?", "Connect", "Cancel");
if (!result)
return;
//Stop Scanner
await adapter.StopScanningForDevicesAsync();
try
{
await adapter.ConnectToDeviceAsync(device);
await DisplayAlert("Message", "Connect Status:" + device.State, "OK");
}
catch (DeviceConnectionException ex)
{
await DisplayAlert("Error", ex.Message, "OK");
}
}
}
}
Upvotes: 0
Reputation: 181
After various try I got the solution:
To read effectively the data from the BLE module, is necessary to use the ValueUpdateHandler.
Then I changed my code in this way:
public partial class BluetoothPage : ContentPage
{
IAdapter adapter;
ObservableCollection<IDevice> devicesList;
ListView pageList;
List<byte> buffer = new List<byte>();
public BluetoothPage()
{
adapter = CrossBluetoothLE.Current.Adapter;
deviceList = new ObservableCollection<IDevice>();
pageList = new ListView();
pageList.ItemSource = deviceList;
pageList.ItemSelected += ActionConnect;
pageStackLayout.Children.Add(pageList); // Layout component declared on BluetoothPage.xaml
}
private void UpdatingValue(object sender, CharacteristicUpdateEventArgs args)
{
buffer.AddRange(args.Characteristic.Value);
}
private void ButtonScan(object sender, EventArgs e)
{
try
{
devicesList.Clear();
adapter.DeviceDiscovered += (s, a) =>
{
devicesList.Add(a.Device);
};
if(!adapter.isScanning)
{
await adapter.StartScanningForDevicesAsync();
}
}
catch(Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
}
private async void ActionConnect(object sender, SelectedItemChangedEventArgs se)
{
if(se != null)
{
try
{
if(adapter.IsScanning)
{
await adapter.StopScanningForDevicesAsync();
}
await adapter.ConnectToDeviceAsync((IDevice)pageList.SelectedItem);
IDevice device = adapter.ConnectedDevices[0];
// now get the service and characteristics of connected device
IService service = device.GetServiceAsync(Guid.Parse("0000ffe0-1000-8000-00805f9b34fb"));
ICharacteristic characteristic = service.GetCharacteristicAsync(Guid.Parse("0000ffe1-1000-8000-00805f9b34fb"));
// we attach the UpdateVale event to the characteristic
// and we start the service
characteristic.ValueUpdated += UpdatingValue;
await characteristic.StartUpdatesAsync();
// now we can write and hopefully read values
byte[] data = { Coderequest.InitRequest, Coderequest.Info }; // My message to sendo to the machine to trigger his functions and his response
await characteristic.WriteAsync(data); // Send the data
}
catch(Exception ex)
{
Console.WriteLine("ERROR: " + ex.Message);
}
}
}
}
So first I create a byte buffer to store the data: List<byte> buffer = new List<byte>()
.
I create a method for the event to catch the value readed and fill the buffer
private void UpdatingValue(object sender, CharacteristicUpdateEventArgs args)
{
buffer.AddRange(args.Characteristic.Value);
}
Finally I attach the method to the characteristic's EventHandler and start the service
characteristic.ValueUpdated += UpdatingValue;
await characteristic.StartUpdatesAsync();
Now every time the module send data to my app, the UpdateEvent triggers and fill the buffer, then print the buffer or show in some views to see the result.
Upvotes: 4
Reputation: 9209
BLE communication can be a pain particularly with Xamarin.
In any case asynchronous calls from your application may return before data has actually been sent to your end device. This is due to several factors:
Most implementations of two way communication between devices using BLE use a minimum of two characteristics, one for data being sent and the other for data being received by a device e.g. device 1 receives data on the characteristic that device 2 sends on and vice versa. This avoids the possibility of any collision of data being sent by both parties.
You could try placing a delay in your code prior to polling for characteristic data and see if that helps, but its not an optimal solution.
I see that you are using your characteristic "anonymously", rather than using its uuid to select it. If the Bluetooth service supports more than one characteristic then you will need to use the uuid to ensure that you are using the correct one. The list of characteristics, much like the list of services and devices may not be returned in the same order whenever you request them.
You have the option of polling characteristics by reading them on a timed basis or setting up handlers for characteristic notification/indication events if the characteristic supports it and your BLE device uses that mechanism to show that data is ready.
Upvotes: 1