1. 建立工程 bh003_ble
源码
2. 添加 nuget 包
- [/code]BlazorHybrid.Maui.Permissions 因为源码比较长,主要是一些检查和申请权限,BLE权限相关代码,就不占用篇幅列出,感兴趣的同学直接打开[url=https://github.com/densen2014/BlazorHybrid/tree/master/BlazorHybrid.Maui.Permissions?WT.mc_id=DT-MVP-5005078]源码[/url]参考
- 顺便打开可空 enable
- [size=4]3. 添加蓝牙权限[/size]
- [size=3]安卓[/size]
- AndroidManifest.xml
- [code]<?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.Components
复制代码 7. 添加服务
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
- {
- [Inject, NotNull]
- BluetoothLEServices? MyBleTester { get; set; }
- [Inject, NotNull] protected IStorage? Storage { get; set; }
- [Inject, NotNull] 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.razor10. 运行



11. 相关资料
如何远程调试 MAUI blazor / Blazor Hybrid
https://www.cnblogs.com/densen2014/p/16988516.html
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |