我被这个关于从我的BLE设备读取一些数据的任务卡住了。
我的目标机器上有一个HM-19 DSD Tech蓝牙LE模块,我想用我的智能手机与它通信。
我使用Xamarin插件。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);
}
}
}
}
当我启动我的应用程序:
- 我触发扫描按钮,它工作得很好
- 然后我点击我想要的BLE设备,它连接完美 在目标机器上使用调试器,我可以看到数据确实是由应用程序通过BLE发送的
但是我没有得到预期的响应,我总是读取相同的字节(可能是默认值),这不是我期望的字节。
奇怪的是,如果我为HM-19模块使用DSD技术演示应用程序并执行相同的指令(连接,发送和读取),它就会工作!我发送触发机器响应的数据,演示应用程序显示我从机器发送的预期正确字节。
所以…我怎么读取这些数据?在开发者网站上,文档几乎没有指导你扫描和连接,但是关于写/读缺乏信息。这是非常令人失望的,这个插件是最好的Nuget库。
谁能帮帮我,我做得不好吗?这是一个插件的链接,也许我错过了什么https://github.com/xabre/xamarin-bluetooth-le
经过多次尝试,我得到了解决方案:
要有效地从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);
}
最后,我将该方法附加到特性的EventHandler上,并启动服务
characteristic.ValueUpdated += UpdatingValue;
await characteristic.StartUpdatesAsync();
现在每次模块发送数据到我的应用程序,UpdateEvent触发并填充缓冲区,然后打印缓冲区或显示在一些视图中查看结果。
BLE通信可能是一种痛苦,尤其是Xamarin。
在任何情况下,应用程序的异步调用可能在数据实际发送到终端设备之前返回。这是由以下几个因素造成的:
- 操作系统BLE驱动程序行为-您在应用程序代码中进行的任何调用都由操作系统驱动程序处理。这些可能在BLE硬件实际处理任何数据之前返回。
- BLE传输层延迟- BLE传输数据不是即时的。两个设备之间的数据连接实际上发生在离散的时隙中,在此期间关闭BLE收发器以节省电力。
- 终端响应时间。
使用BLE的设备之间的双向通信的大多数实现使用至少两个特征,一个用于发送数据,另一个用于设备接收数据,例如设备1接收设备2发送的特征数据,反之亦然。这避免了双方发送的数据发生冲突的可能性。
您可以尝试在轮询特征数据之前在代码中放置延迟,看看是否有帮助,但这不是最佳解决方案。
我看到你正在"匿名"使用你的特征,而不是使用它的uid来选择它。如果蓝牙服务支持多个特性,那么您需要使用uid来确保使用的是正确的特性。特征列表,就像服务和设备列表一样,可能不会在你请求它们时以相同的顺序返回。
您可以选择通过定时读取特征或设置特征通知/指示事件的处理程序来轮询特征,如果特征支持它并且您的BLE设备使用该机制来显示数据已准备好。
程序不能正常工作,所以我查了一下,发现了类似的东西,缺少的不是找到蓝牙设备。很抱歉,如果你可以分享,请把整个程序用电子邮件发送过来。谢谢你!kdg000@empas.com
- AndroidManifest.xml
-
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
employeeID名称空间{公共部分类MainPage: ContentPage{IAdapter适配器;IBluetoothLE祝福;ObservableCollection devicelist;设备;i系设备之
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");
}
}
}
}