Cosimo Davide Viggiano
Cosimo Davide Viggiano

Reputation: 181

Xamarin Plugin.BLE why data read doesn't change?

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

Answers (3)

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

  1. AndroidManifest.xml
  1. 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>
    
  2. 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

Cosimo Davide Viggiano
Cosimo Davide Viggiano

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

ChrisBD
ChrisBD

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:

  1. Operating system BLE driver behaviour - Any call that you make in your application code is handled by the operating system drivers. These may return before any data is actually handled by the BLE hardware.
  2. BLE transport layer delays - BLE transmission of data is not instant. The data connection between two devices actually occurs within discrete time slots, between which the BLE transceiver is turned off to save power.
  3. End device response time.

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

Related Questions