Xamarin Plugin.BLE 为什么读取的数据没有改变?

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

我无法完成从我的 BLE 设备读取一些数据的任务。

我的目标机器上有一个 HM-19 DSD Tech Bluetooth LE 模块,我想用我的智能手机与它通信。

我正在使用 Xamarin 和 Plugin.BLE 来尝试实现这一点。

有我的BluetoothPage.xaml.cs代码

  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);
              }
          }
      }
  }

当我启动我的应用程序时:

但我没有得到预期的响应,我读取的总是与我预期的字节不同的字节(可能是默认值)。

奇怪的是,如果我使用 HM-19 模块的 DSD 技术演示应用程序并执行相同的指令(连接、发送和读取),它就可以工作!我发送触发机器响应的数据,演示应用程序显示我从机器发送的预期正确字节。

那么...我如何读取该数据?在开发者网站上,文档几乎没有指导您如何扫描和连接,但在 write/read 上缺少信息。非常令人失望,这个插件是 Nuget 存储库中最好的。

任何人都可以帮助我,我做的不好吗?

这是一个link插件,可能我漏掉了什么https://github.com/xabre/xamarin-bluetooth-le

BLE 通信可能会很痛苦,尤其是使用 Xamarin 时。

在任何情况下,您的应用程序的异步调用可能 return 在数据实际发送到您的终端设备之前。 这是由以下几个因素造成的:

  1. 操作系统 BLE 驱动程序行为 - 您在应用程序代码中进行的任何调用都由操作系统驱动程序处理。这些可能 return 在 BLE 硬件实际处理任何数据之前。
  2. BLE 传输层延迟 - BLE 数据传输不是即时的。两个设备之间的数据连接实际上发生在离散的时隙内,其间 BLE 收发器被关闭以节省电力。
  3. 结束设备响应时间。

使用 BLE 的设备之间的双向通信的大多数实现至少使用两个特征,一个用于发送数据,另一个用于设备接收数据,例如设备 1 接收有关设备 2 发送的特性数据,反之亦然。这避免了双方发送数据发生冲突的可能性。

您可以尝试在轮询特征数据之前在代码中放置一个延迟,看看是否有帮助,但这不是最佳解决方案。

我看到您正在“匿名”使用您的特征,而不是使用它的 uuid select。如果蓝牙服务支持不止一种特性,那么您将需要使用 uuid 来确保您使用的是正确的特性。特征列表,很像服务和设备列表,可能不会在您请求它们时按相同顺序return编辑。

您可以选择通过定时读取特征来轮询特征,或者为特征 notification/indication 事件设置处理程序(如果特征支持它并且您的 BLE 设备使用该机制来显示数据已准备就绪)。

经过各种尝试我得到了解决方案:

为了有效地从BLE模块读取数据,需要使用ValueUpdateHandler。

然后我这样修改了我的代码:

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);
              }
          }
      }
  }

所以我首先创建一个字节缓冲区来存储数据:List<byte> buffer = new List<byte>()。 我为事件创建了一个方法来捕获读取的值并填充缓冲区

private void UpdatingValue(object sender, CharacteristicUpdateEventArgs args)
{
    buffer.AddRange(args.Characteristic.Value);
}

最后我将方法附加到特性的事件处理程序并启动服务

characteristic.ValueUpdated += UpdatingValue;
await characteristic.StartUpdatesAsync();

现在每次模块向我的应用程序发送数据时,UpdateEvent 都会触发并填充缓冲区,然后打印缓冲区或在某些视图中显示以查看结果。