尘世间的过客 发表于 2023-8-23 01:02:31

bh003- Blazor hybrid / Maui 使用蓝牙BLE快速教程

1. 建立工程 bh003_ble

源码
2. 添加 nuget 包

BlazorHybrid.Maui.Permissions 因为源码比较长,主要是一些检查和申请权限,BLE权限相关代码,就不占用篇幅列出,感兴趣的同学直接打开源码参考
顺便打开可空 enable
3. 添加蓝牙权限

安卓

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>







<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

</manifest>iOS

Info.plist
    <key>UIBackgroundModes</key>
    <array>
      <string>bluetooth-central</string>
      <string>bluetooth-peripheral</string>
    </array>
    <key>NSBluetoothPeripheralUsageDescription</key>
    <string>此应用程序需要访问您的蓝牙。请根据要求授予权限.</string>
    <key>NSBluetoothAlwaysUsageDescription</key>
    <string>此应用程序需要访问您的蓝牙。请根据要求授予权限.</string>以下是完整文件
    LSRequiresIPhoneOS      UIDeviceFamily            1      2      UIRequiredDeviceCapabilities            arm64      UISupportedInterfaceOrientations            UIInterfaceOrientationPortrait      UIInterfaceOrientationLandscapeLeft      UIInterfaceOrientationLandscapeRight      UISupportedInterfaceOrientations~ipad            UIInterfaceOrientationPortrait      UIInterfaceOrientationPortraitUpsideDown      UIInterfaceOrientationLandscapeLeft      UIInterfaceOrientationLandscapeRight      XSAppIconAssets    Assets.xcassets/appicon.appiconset    <key>UIBackgroundModes</key>
    <array>
      <string>bluetooth-central</string>
      <string>bluetooth-peripheral</string>
    </array>
    <key>NSBluetoothPeripheralUsageDescription</key>
    <string>此应用程序需要访问您的蓝牙。请根据要求授予权限.</string>
    <key>NSBluetoothAlwaysUsageDescription</key>
    <string>此应用程序需要访问您的蓝牙。请根据要求授予权限.</string>Windows

Package.appxmanifest

4. 编辑 Index.html 文件,引用 BootstrapBlazor UI 库.

完整文件
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
    <title>bh003_ble</title>
    <base href="/" />
    <link href="_content/BootstrapBlazor.FontAwesome/css/font-awesome.min.css" rel="stylesheet">
    <link href="_content/BootstrapBlazor/css/bootstrap.blazor.bundle.min.css" rel="stylesheet">
    <link href="_content/BootstrapBlazor/css/motronic.min.css" rel="stylesheet">
    <link href="css/app.css" rel="stylesheet" />
    <link href="bh003_ble.styles.css" rel="stylesheet" />
</head>

<body>

   

    Loading...

   
      An unhandled error has occurred.
      <a target="_blank" href="https://www.cnblogs.com/" >Reload</a>
      <a ><i ></i></a>
   

   
   

</body>

</html>
5. 添加 BootstrapBlazorRoot 组件

Main.razor 文件添加 BootstrapBlazorRoot 组件

6. 添加命名空间引用

_Imports.razor
@using BootstrapBlazor.Components7. 添加服务

MauiProgram.cs
添加
            builder.Services.AddDensenExtensions();
            builder.Services.ConfigureJsonLocalizationOptions(op =>
            {
                // 忽略文化信息丢失日志
                op.IgnoreLocalizerMissing = true;

            });
            builder.Services.AddSingleton<BluetoothLEServices>();
            builder.Services.AddScoped<IStorage, StorageService>();完整文件
using bh003_ble.Data;using Microsoft.Extensions.Logging;using BlazorHybrid.Maui.Shared;using BootstrapBlazor.WebAPI.Services;namespace bh003_ble{    public static class MauiProgram    {      public static MauiApp CreateMauiApp()      {            var builder = MauiApp.CreateBuilder();            builder                .UseMauiApp()                .ConfigureFonts(fonts =>                {                  fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");                });            builder.Services.AddMauiBlazorWebView();#if DEBUG                builder.Services.AddBlazorWebViewDeveloperTools();                builder.Logging.AddDebug();#endif            builder.Services.AddSingleton();            builder.Services.AddDensenExtensions();
            builder.Services.ConfigureJsonLocalizationOptions(op =>
            {
                // 忽略文化信息丢失日志
                op.IgnoreLocalizerMissing = true;

            });
            builder.Services.AddSingleton<BluetoothLEServices>();
            builder.Services.AddScoped<IStorage, StorageService>();            return builder.Build();      }    }}8. 添加代码后置文件 Pages/Index.razor.cs

Index.razor.cs
using BlazorHybrid.Core.Device;
using BlazorHybrid.Maui.Shared;
using BootstrapBlazor.Components;
using BootstrapBlazor.WebAPI.Services;
using Microsoft.AspNetCore.Components;
using System.Diagnostics.CodeAnalysis;

namespace bh003_ble.Pages;

public partial class Index : IAsyncDisposable
{
   
    BluetoothLEServices? MyBleTester { get; set; }
    protected IStorage? Storage { get; set; }
    protected ToastService? ToastService { get; set; }

    public void SetTagDeviceName(BleTagDevice ble)
    {
      MyBleTester.TagDevice = ble;

      if (!isInit)
      {
            MyBleTester.OnMessage += OnMessage;
            MyBleTester.OnDataReceived += OnDataReceived;
            MyBleTester.OnStateConnect += OnStateConnect;
            isInit = true;
      }
    }

    public event Action<string>? OnMessage;
    public event Action<string>? OnDataReceived;
    public event Action<bool>? OnStateConnect;

    bool isInit = false;

    public async Task<List<BleDevice>?> StartScanAsync() => await MyBleTester.StartScanAsync();

    public async Task<List<BleService>?> ConnectToKnownDeviceAsync(Guid deviceID, string? deviceName = null) => await MyBleTester.ConnectToKnownDeviceAsync(deviceID, deviceName);

    public async Task<List<BleCharacteristic>?> GetCharacteristicsAsync(Guid serviceid) => await MyBleTester.GetCharacteristicsAsync(serviceid);

    public async Task<string?> ReadDeviceName(Guid? serviceid, Guid? characteristic) => await MyBleTester.ReadDeviceName(serviceid, characteristic);

    public async Task<byte[]?> ReadDataAsync(Guid characteristic) => await MyBleTester.ReadDataAsync(characteristic);

    public async Task<bool> SendDataAsync(Guid characteristic, byte[] ary) => await MyBleTester.SendDataAsync(characteristic, ary);

    public async Task<bool> DisConnectDeviceAsync() => await MyBleTester.DisConnectDeviceAsync();


    public Task<bool> BluetoothIsBusy() => MyBleTester.BluetoothIsBusy();




    private bool IsScanning = false;
    private List<BleDevice>? Devices { get; set; }
    private List<BleService>? Services { get; set; }
    private List<BleCharacteristic>? Characteristics { get; set; }
    private string? ReadResult { get; set; }
    private string? Message { get; set; } = "";

    BleTagDevice BleInfo { get; set; } = new BleTagDevice();

    private List<SelectedItem> DemoList { get; set; } = new List<SelectedItem>() { new SelectedItem() { Text = "测试数据", Value = "" } };
    private List<SelectedItem> DeviceList { get; set; } = new List<SelectedItem>();
    private List<SelectedItem> ServiceidList { get; set; } = new List<SelectedItem>();
    private List<SelectedItem> CharacteristicList { get; set; } = new List<SelectedItem>();

    private Dictionary<string, object>? IsScanningCss => IsScanning ? new() { { "disabled", "" }, } : null;

    bool IsAutoConnect { get; set; }
    bool IsAuto { get; set; }
    bool IsInit { get; set; }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
      if (firstRender)
      {
            await Init();
      }
    }

    async Task<bool> Init()
    {
      try
      {

            if (IsInit) return true;

            if (await BluetoothIsBusy())
            {
                await ToastService.Warning("蓝牙正在使用中,请稍后再试");
                return false;
            }
            OnMessage += Tools_OnMessage;
            OnDataReceived += Tools_OnDataReceived;
            OnStateConnect += Tools_OnStateConnect;
            SetTagDeviceName(BleInfo);
            IsInit = true;

            StateHasChanged();

            var deviceID = await Storage.GetValue("bleDeviceID", string.Empty);
            if (!string.IsNullOrEmpty(deviceID))
            {
                BleInfo.Name = await Storage.GetValue("bleDeviceName", string.Empty);
                BleInfo.DeviceID = Guid.Parse(deviceID);
                var serviceid = await Storage.GetValue("bleServiceid", string.Empty);
                if (!string.IsNullOrEmpty(serviceid)) BleInfo.Serviceid = Guid.Parse(serviceid);
                var characteristic = await Storage.GetValue("bleCharacteristic", string.Empty);
                if (!string.IsNullOrEmpty(characteristic)) BleInfo.Characteristic = Guid.Parse(characteristic);
                var auto = await Storage.GetValue("bleAutoConnect", string.Empty);
                if (auto == "True")
                {
                  IsAuto = true;
                  await AutoRead();

                }
            }
            return true;

      }
      catch (Exception ex)
      {
            System.Console.WriteLine(ex.Message);
      }
      return false;
    }


    private async Task AutoRead()
    {
      Services = null;
      Characteristics = null;
      Message = "";
      ReadResult = "";
      Devices = new List<BleDevice>() { new BleDevice() { Id = BleInfo.DeviceID, Name = BleInfo.Name } };
      DeviceList = new List<SelectedItem>() { new SelectedItem() { Text = BleInfo.Name, Value = BleInfo.DeviceID.ToString() } };
      IsAutoConnect = true;
      await OnDeviceSelect();
      IsAutoConnect = false;
    }

    private async Task OnStateChanged(bool value)
    {
      await Storage.SetValue("bleAutoConnect", value.ToString());
    }

    private void Tools_OnStateConnect(bool obj)
    {

    }

    private async void Tools_OnDataReceived(string message)
    {
      ReadResult = message;
      Tools_OnMessage(message);
      await InvokeAsync(StateHasChanged);
    }

    private async void Tools_OnMessage(string message)
    {
      if (Message != null && Message.Length > 500) Message = Message.Substring(0, 500);
      Message = $"{message}\r\n{Message}";
      await InvokeAsync(StateHasChanged);
    }


    //扫描外设
    private async void ScanDevice()
    {
      if (!await Init()) return;

      IsScanning = true;
      Devices = null;
      Services = null;
      Characteristics = null;
      Message = "";
      ReadResult = "";
      DeviceList = new List<SelectedItem>() { new SelectedItem() { Text = "请选择...", Value = "" } };

      //开始扫描
      Devices = await StartScanAsync();

      if (Devices != null)
      {
            Devices.ForEach(a => DeviceList.Add(new SelectedItem() { Active = IsAutoConnect && a.Id == BleInfo.DeviceID, Text = a.Name ?? a.Id.ToString(), Value = a.Id.ToString() }));
      }

      IsScanning = false;

      //异步更新UI
      await InvokeAsync(StateHasChanged);
    }

    //连接外设
    private async Task OnDeviceSelect(SelectedItem item)
    {
      if (IsAutoConnect || item.Value == "") return;
      BleInfo.Name = item.Text;
      BleInfo.DeviceID = Guid.Parse(item.Value);
      await OnDeviceSelect();
    }

    private async Task OnDisConnectDevice()
    {
      if (await DisConnectDeviceAsync())
            await ToastService.Success("断开成功");
      else
            await ToastService.Error("断开失败");
    }

    private async Task OnDeviceSelect()
    {

      Services = null;
      Characteristics = null;
      Message = "";
      ReadResult = "";
      ServiceidList = new List<SelectedItem>() { new SelectedItem() { Text = "请选择...", Value = "" } };
      //连接外设
      Services = await ConnectToKnownDeviceAsync(BleInfo.DeviceID, BleInfo.Name);
      if (Services != null)
      {
            Services.ForEach(a => ServiceidList.Add(new SelectedItem() { Active = IsAutoConnect && a.Id == BleInfo.Serviceid, Text = a.Name ?? a.Id.ToString(), Value = a.Id.ToString() }));
            await Storage.SetValue("bleDeviceID", BleInfo.DeviceID.ToString());
            await Storage.SetValue("bleDeviceName", BleInfo.Name ?? "上次设备");
            if (BleInfo.Serviceid != Guid.Empty && IsAutoConnect)
            {
                await OnServiceidSelect();
            }
      }

      //异步更新UI
      await InvokeAsync(StateHasChanged);
    }


    private async Task OnServiceidSelect(SelectedItem item)
    {
      if (IsAutoConnect || item.Value == "") return;
      BleInfo.Serviceid = Guid.Parse(item.Value);
      await OnServiceidSelect();
    }
    private async Task OnServiceidSelect()
    {
      Characteristics = null;
      Message = "";
      ReadResult = "";
      CharacteristicList = new List<SelectedItem>() { new SelectedItem() { Text = "请选择...", Value = "" } };
      Characteristics = await GetCharacteristicsAsync(BleInfo.Serviceid);
      if (Characteristics != null)
      {
            Characteristics.ForEach(a => CharacteristicList.Add(new SelectedItem() { Active = IsAutoConnect && a.Id == BleInfo.Characteristic, Text = a.Name ?? a.Id.ToString(), Value = a.Id.ToString() }));
            await Storage.SetValue("bleServiceid", BleInfo.Serviceid.ToString());
            if (BleInfo.Characteristic != Guid.Empty && IsAutoConnect)
            {
                await ReadDeviceName();
            }
      }
      await InvokeAsync(StateHasChanged);
    }

    private async Task OnCharacteristSelect(SelectedItem item)
    {
      if (IsAutoConnect) return;
      BleInfo.Characteristic = Guid.Parse(item.Value);
      await ReadDeviceName();
    }

    //读取数值
    private async Task ReadDeviceName()
    {
      Message = "";

      //读取数值
      ReadResult = await ReadDeviceName(BleInfo.Serviceid, BleInfo.Characteristic);
      await Storage.SetValue("bleCharacteristic", BleInfo.Characteristic.ToString());

      if (!string.IsNullOrEmpty(ReadResult)) await ToastService.Information("读取成功", ReadResult);
      //异步更新UI
      await InvokeAsync(StateHasChanged);
    }

    private async Task ReadDataAsync()
    {
      Message = "";
      //读取数值
      var res = await ReadDataAsync(BleInfo.Characteristic);
      if (!string.IsNullOrEmpty(ReadResult)) await ToastService.Information("读取成功", res.ToString());

      //异步更新UI
      await InvokeAsync(StateHasChanged);
    }

    private async Task SendDataAsync()
    {
      Message = "";
      //读取数值
      var res = await SendDataAsync(BleInfo.Characteristic, null);
      await ToastService.Information("成功发送", res.ToString());

      //异步更新UI
      await InvokeAsync(StateHasChanged);
    }

    ValueTask IAsyncDisposable.DisposeAsync()
    {
      OnMessage -= Tools_OnMessage;
      OnDataReceived -= Tools_OnDataReceived;
      OnStateConnect -= Tools_OnStateConnect;
      return new ValueTask();
    }

}9. 添加 UI Pages/Index.razor

Index.razor
@page "/"蓝牙

                  @if (Devices != null)      {<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>







<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

</manifest>@if (Characteristics != null)            {<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>







<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

</manifest>          }      }    @if (Devices != null){<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>







<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

</manifest>      @if (Services != null)      {<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>







<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

</manifest>                  @if (Characteristics != null)            {<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>







<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

</manifest><?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>







<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

</manifest>@if (ReadResult != null)                {<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>







<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

</manifest><?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />


<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>







<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation"/>

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

</manifest>            }            }      }    }@if (BleInfo.Name != null){            历史连接
      @BleInfo.Name
      @BleInfo.DeviceID
      @BleInfo.Serviceid
      @BleInfo.Characteristic
      @ReadResult
    }@Message10. 运行




11. 相关资料

如何远程调试 MAUI blazor / Blazor Hybrid
https://www.cnblogs.com/densen2014/p/16988516.html

来源:https://www.cnblogs.com/densen2014/archive/2023/08/23/17649961.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: bh003- Blazor hybrid / Maui 使用蓝牙BLE快速教程