nanoFramework.Device.Bluetooth/README.zh-cn.md

22 KiB
Исходник Постоянная ссылка Ответственный История

Quality Gate Status Reliability Rating License NuGet #yourfirstpr Discord

nanoFramework logo


文档语言: English | 简体中文

欢迎来到 .NET nanoFramework nanoFramework.Device.Bluetooth 类库

构建状态

Component Build Status NuGet Package
nanoFramework.Device.Bluetooth 构建状态 NuGet

nanoFramework.Device.Bluetooth 类库

蓝牙低能耗库

该库基于Windows.Devices.Bluetooth UWP类库但经过简化并与异步相关的调用进行同步。 最初的.net程序集依赖于Windows.Storage.Streams for DataReader和DataWriter;这个库简化了内建版本。 所以.net UWP示例中的IBuffer现在应该使用Buffer代替。

我们还为该程序集添加了一个扩展允许向ServiceProvider添加额外的服务而不限制类型。

固件版本

蓝牙目前仅支持ESP32设备与以下固件。

  • ESP32_BLE_REV0
  • ESP32_BLE_REV3
  • M5Core2

此限制是由于固件映像中的IRAM内存空间。 对于ESP32设备的第1版PSRAM实现需要大量的PSRAM补丁这大大减少了 IRAM区域有可用空间所以目前ESP32_BLE_REV0禁用了PSRAM。与3版设备的蓝牙和 PSRAM都是可用的。

示例

许多蓝牙示例可在 nanoFramework示例在这里

使用

概述

此实现支持Gatt服务器和Gatt客户端实现的简化版本。 设备既可以作为服务器运行,也可以作为客户端运行,但不能同时运行。

例如如果你启动一个Watcher来寻找来自服务器设备的广告你就不会这样做 能够连接到这些设备直到观察者停止。但你可以从connected接收数据 设备,而监视程序正在扫描。

更多信息请参见相关章节:-

  • Gatt Server
  • Gatt Client / Central

也是该程序集的一部分的是NordicSPP类它实现了基于的串行协议配置文件 北欧的规范。这使客户端可以轻松地通过蓝牙连接LE发送和接收消息通过一个 蓝牙串行终端应用。一个常见的用例是提供设备。参见后面的SPP部分了解用法。

属性和uuid

每个服务、特征和描述符都由它自己唯一的128位UUID定义。这些都是 叫做GUID。这些在蓝牙规范中称为UUID。

如果该属性是蓝牙SIG定义的标准UUID则它还将具有相应的16位短ID(例如, 特征Battery Level的UUID为00002A19-0000-1000-8000-00805F9B34FB短ID为0x2A19)。 通用的标准uuid可以在gatserviceuuid和gatcharacteruuids中看到。

如果短ID没有出现在GattServiceUuids或gattcharacteristicuids中那么通过以下方法创建自己的短GUID 调用实用函数CreateUuidFromShortCode。

Guid uuid1 = Utility.CreateUuidFromShortCode(0x2A19);

Gatt Server

定义服务和关联的特征

GattServiceProvider 用于创建和发布主服务定义。将自动创建一个额外的设备信息服务。

GattServiceProviderResult result = GattServiceProvider.Create(uuid);
if (result.Error != BluetoothError.Success)
{
    return result.Error;
}

serviceProvider = result.ServiceProvider;

现在向服务添加所有必需的特征和描述符。 目前只支持读、写、无响应写、通知和指示特征。

添加读特性

如果用户描述被添加到gatlocalcharacteristicparameters中那么用户描述描述符将自动添加到特征中。 对于读取特征,您将需要一个关联的事件处理程序来为读取提供数据。

GattLocalCharacteristicParameters ReadParameters = new GattLocalCharacteristicParameters
{
    CharacteristicProperties = (GattCharacteristicProperties.Read),
    UserDescription = "我的阅读特点"
};

GattLocalCharacteristicResult characteristicResult = serviceProvider.Service.CreateCharacteristic(uuid1, ReadParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
    // 一个错误发生。
    return characteristicResult.Error;
}

_readCharacteristic = characteristicResult.Characteristic;
_readCharacteristic.ReadRequested += _readCharacteristic_ReadRequested;

您可以通过设置StaticValue属性来获得具有常量值的读特性。

// 设置特征的Int 16常量值。
DataWriter dr = new DataWriter();
dr.WriteInt16(123);

GattLocalCharacteristicParameters ReadParameters = new GattLocalCharacteristicParameters
{
    CharacteristicProperties = (GattCharacteristicProperties.Read),
    UserDescription = "我的阅读特点",
    StaticValue = dr.DetachBuffer()
};

如果设置了StaticValue,则不会调用读事件,也不需要定义该事件。

添加写或无响应写特性

写特性用于从客户端接收数据。

GattLocalCharacteristicParameters WriteParameters = new GattLocalCharacteristicParameters
{
    CharacteristicProperties = GattCharacteristicProperties.Write,
    UserDescription = "我的阅读特点",
};


characteristicResult = serviceProvider.Service.CreateCharacteristic(uuid2, WriteParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
    // 一个错误发生。
    return characteristicResult.Error;
}
_writeCharacteristic = characteristicResult.Characteristic;
_writeCharacteristic.WriteRequested += _writeCharacteristic_WriteRequested;

添加通知特性

notify特征用于在值发生更改时自动通知订阅的客户端。

GattLocalCharacteristicParameters NotifyParameters = new GattLocalCharacteristicParameters
{
    CharacteristicProperties = GattCharacteristicProperties.Notify,
    UserDescription = "我的阅读特点",
};

characteristicResult = serviceProvider.Service.CreateCharacteristic(uuid3, NotifyParameters);
if (characteristicResult.Error != BluetoothError.Success)
{
    // 一个错误发生。
    return characteristicResult.Error;
}

_notifyCharacteristic = characteristicResult.Characteristic;
_notifyCharacteristic.SubscribedClientsChanged += _notifyCharacteristic_SubscribedClientsChanged;

向Notify特征发送数据

通过调用notify特性上的NotifyValue方法可以将数据发送到订阅的客户机。 可以添加额外的检查,以便仅在有订阅客户端或值发生更改时才发送值 自从去年收到通知。

private static void UpdateNotifyValue(double newValue)
{
    DataWriter dw = new DataWriter();
    dw.WriteDouble(newValue);

    _notifyCharacteristic.NotifyValue(dw.DetachBuffer());
}

事件

读请求事件

当客户端请求读取特征时,假定未设置静态值,托管事件将被调用。

如果没有设置事件处理程序或您没有及时响应,将向客户端返回一个不太可能的蓝牙错误。

如果从外围设备读取值需要时间,那么最好将其放在事件处理程序之外。

这显示了向客户端请求返回2个值。

private static void _readCharacteristic_ReadRequested(GattLocalCharacteristic sender, GattReadRequestedEventArgs ReadRequestEventArgs)
{
    GattReadRequest request = ReadRequestEventArgs.GetRequest();

    // 创建DataWriter并将数据写入缓冲区
    DataWriter dw = new DataWriter();
    dw.WriteInt16(1);
    dw.WriteInt32(2);

    request.RespondWithValue(dw.DetachBuffer());

    // 如果存在某种类型的错误,则用错误响应
    //request.RespondWithProtocolError((byte)BluetoothError.DeviceNotConnected);
}

写请求事件

当数据被发送到写特性时,托管事件被调用。如果没有事件处理程序 设置或您没有及时响应,将返回一个不可能的蓝牙错误给客户端。

接收到的数据是一个字节数组并按特性的要求格式化。这可能是一个单打Int16, Int32, string等也可以是多个不同的值。

这将显示从缓冲区读取单个Int32值如果数值错误则返回错误 已提供的字节数。

private static void _writeCharacteristic_WriteRequested(GattLocalCharacteristic sender, GattWriteRequestedEventArgs WriteRequestEventArgs)
{
    GattWriteRequest request = WriteRequestEventArgs.GetRequest();

    // 检查预期数据长度
    if (request.Value.Length != 4)
    {
        request.RespondWithProtocolError((byte)BluetoothError.NotSupported);
        return;
    }

    // 从所需格式的缓冲区读取数据
    DataReader rdr = DataReader.FromBuffer(request.Value);
    Int32 data = rdr.ReadInt32();

    // 对接收到的数据做些什么
    Debug.WriteLine($"Rx data::{data}");

    // 如果Write需要响应则响应
    if (request.Option == GattWriteOption.WriteWithResponse)
    {
        request.Respond();
    }
}

订阅的客户端更改事件

对于可通知的特征,客户端可以订阅以接收通知值。当客户端 订阅将被调用的托管事件。 特征的SubscribedClients数组包含连接的客户机。

private static void _notifyCharacteristic_SubscribedClientsChanged(GattLocalCharacteristic sender, object args)
{
    if ( sender.SubscribedClients.Length > 0)
    {
         Debug.WriteLine($"客户端连接");
    }
}

添加额外的服务

您可以添加或替换现有的服务,对添加的服务没有任何限制。 添加蓝牙标准的示例请参见蓝牙示例3 服务、设备信息、当前时间、电池电量和环境传感器。

// 电池服务显示当前电池电量百分比
BatteryService BatService = new BatteryService(serviceProvider);

// 当电池服务被读取时,用当前电池电量更新电池服务。
BatService.BatteryLevel = 94;

广告服务

一旦创建了所有的特征,你需要发布服务,以便其他设备可以看到它 和/或连接到它。我们还提供在发现中看到的设备名称。

serviceProvider.StartAdvertising(new GattServiceProviderAdvertisingParameters()
{
    DeviceName = "我的例子设备",
    IsConnectable = true,
    IsDiscoverable = true
});

Gatt Client / Central

蓝牙LE客户端用于从设备(服务器)中查找广告并连接到这些广告 设备和读取和写入值的特性。您可以设置通知事件,以便自动 当值发生变化时获得通知。

我们有2个样品可供选择:

  • Central1 -这个直接的样本,只看广告和打印结果。
  • Central2 -这是一个完整的示例,是一个关于如何在这种情况下收集温度值的示例 来自一堆带有环境传感器服务的设备。这些设备是Sample设备示例的更新版本。

监听广告

要监听广告可以使用BluetoothLEAdvertisementWatcher类。

    BluetoothLEAdvertisementWatcher watcher = new();
    watcher.Received += Watcher_Received;
    watcher.Start();

当接收到广告时将引发一个事件调用Watcher_Received事件处理程序。

在事件处理程序中,您将能够使用事件上提供的信息选择设备。

这可以是设备LocalName或广告数据中提供的其他数据。

有关更多信息,请参见示例。

你也可以添加过滤器到BluetoothLEAdvertisementWatcher。

目前这只是一个RSSI滤波器所以你只从一定信号强度内的设备接收。

RSSI filter

    watcher.SignalStrengthFilter.InRangeThresholdInDBm = -70;
    watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -77;
    watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(10000);

创建设备并连接到设备

要与设备通信需要使用设备的蓝牙地址和类型创建bluetthledevice类。

这可以是BluetoothLEAdvertisementWatcher事件中的蓝牙地址也可以是使用硬编码的地址。

在本例中,来自观察者的广告接收事件。

BluetoothLEDevice device = BluetoothLEDevice.FromBluetoothAddress(args.BluetoothAddress)
```没有具体的连接方式,查询设备时会自动建立连接

为其服务。ConnectionStatusChanged事件可用于检测连接状态的变化和尝试

重新连接可以通过再次查询设备服务来完成。避免在事件中这样做,因为它会阻碍其他的

在连接期间触发的事件。



您可以返回到“观看”查看广告,但有一个限制:在“观看”停止之前,您不能连接到新发现的设备。

在监视程序运行时,您仍然可以与连接的设备通信。最好的方法是收集表中所有找到的设备,直到Watcher被

停止,然后连接到所有找到的设备。见【示例2(https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/Central2)


在桌面版本中没有公开Close()方法,但是在这个版本中已经实现了它以便更好地控制连接。

## 查询设备服务

查询设备提供的所有服务。如果“GattDeviceServicesResult”状态为“GattCommunicationStatus”。成功后

数组的GattDeviceService对象可以在GattDeviceServicesResult Services属性中获得
```csharp
    GattDeviceServicesResult sr = device.GetGattServices();
    if (sr.Status == GattCommunicationStatus.Success)
    {
        GattDeviceService[] services = sr.Services;

    }

查询设备提供的指定服务。

    GattDeviceServicesResult sr = device.GetGattServicesForUuid(GattServiceUuids.EnvironmentalSensing);
    if (sr.Status == GattCommunicationStatus.Success)
    {
        
    }

以上两种获取服务的方法都将尝试连接到设备。

注意: 服务会被缓存,所以当它们第一次被查询时,它会从设备中检索它们。 对服务的进一步调用将返回缓存的结果。要清除缓存,必须对设备进行处理。

查询服务特点

当您有正确的GattDeviceService对象时您可以查询它以获得所需的features。

与服务一样,有一个方法可以查询所有的特征或仅查询特定的特征。

查询所有特征

    GattCharacteristicsResult cr = service.GetCharacteristics();
    if (cr.Status == GattCommunicationStatus.Success)
    {
        GattCharacteristic[] chars = cr.Characteristics;
    }    

查询具有标准温度UUID的所有特征的服务。

    GattCharacteristicsResult cr = service.GetCharacteristicsForUuid(GattCharacteristicUuids.Temperature);
    if (cr.Status == GattCommunicationStatus.Success)
    {
        GattCharacteristic[] gcs = cr.Characteristics;
    }    

注意:

特征被缓存,所以第一次请求时它将从设备中检索它们。

对同一服务的进一步调用将返回缓存的结果。

查询特征描述符

使用这些方法,描述符可以以与服务和特征相同的方式进行检索

GetDescriptors GetDescriptorsForUuid。

在这些例子中gc是gatcharacteristic对象。

得到所有描述符。

    GattDescriptorsResult dr = gc.GetDescriptors();
    if (dr.Status == GattCommunicationStatus.Success)
    {

    }   

获取具有特定UUID的所有描述符。

    GattDescriptorsResult dr = gc.GetDescriptorsForUuid(uuid);
    if (dr.Status == GattCommunicationStatus.Success)
    {

    }   

属性UserDescriptionPresentationFormats将自动检索描述符

从设备。任何进一步获取描述符的调用都将来自本地缓存。

读取和写入属性值

要从trait或Descriptor中读取值请使用ReadValue()方法。如果成功则返回Buffer对象

将可用在那里可以使用DataReader读取数据。

缓冲区中数据的格式取决于被读取的特征/描述符。

这个例子从Characteristic/Descriptor中读取值并将这3个字节加载到Byte和ushort中。

    GattReadResult rr = gc.ReadValue();
    if (rr.Status == GattCommunicationStatus.Success)
    {
        DataReader rdr = DataReader.FromBuffer(rr.Value);
        Byte data1 = rdr.ReadByte();
        ushort data2 = rdr.ReadInt16();

    }

要写入一个特征或描述符创建一个缓冲区所需的数据并调用WriteValueWithResult()方法。

    DataWriter dw = new();
    dw.WriteBytes(new byte[] { 1, 2, 3, 4 });
    dw.WriteUInt32(23);

    GattWriteResult wr = gc.WriteValueWithResult(dw.DetachBuffer(), , GattWriteOption.WriteWithResponse);
    if (wr.Status == GattCommunicationStatus.Success)
    {

    }

启用值更改通知

这允许在服务器上的Characteristic值发生变化时接收事件。通知是启用的通过设置一个事件然后设置CCCD描述符的值如下所示。

gc是GattCharacteristic要启用的示例。

    // 设置一个通知值更改事件
    gc.ValueChanged += ValueChanged;

    // 并为Notify配置CCCD
    gc.WriteClientCharacteristicConfigurationDescriptorWithResult(GattClientCharacteristicConfigurationDescriptorValue.Notify);

要关闭通知将none值写入CCCD描述符。

通知事件的事件句柄。

发送者是更改来自的gatcharacteristic和valueChangedEventArgs。CharacteristicValue是Buffer值加上新值。

    private static void ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs valueChangedEventArgs)
    {
        DataReader rdr = DataReader.FromBuffer(valueChangedEventArgs.CharacteristicValue);

        // 从DataReader读取值
    }

处理设备错误

对象上的ConnectionStatusChanged事件以处理连接错误BluetoothLEDevice对象。如果连接丢失您可以尝试通过再次请求服务。看到Central2样本。

确保检查所有请求的返回状态,以确保它们是成功的。

蓝牙串口配置文件(SPP)

该程序集具有北欧SPP的实现可以很容易地用于在蓝牙客户机和设备之间发送消息

这是一种简单的方式为设备提供任何额外的信息如WiFi详细信息。

有许多Android和IOS应用程序支持北欧SPP可以用来发送/接收消息。

创建SPP实例

创建SPP实例并为读取消息和客户机连接活动提供事件处理程序。

以设备名称开始发布广告。

使用命名空间 nanoFramework.Device.Bluetooth.Spp

NordicSpp spp = new NordicSpp();
spp.ReceivedData += Spp_ReceivedData;
spp.ConnectedEvent += Spp_ConnectedEvent;

spp.Start("MySpp");

完成后调用Stop方法停止SPP。

处理读数据事件

数据既可以作为字节数组读取,也可以作为字符串读取。

private void Spp_ReceivedData(IBluetoothSpp sender, SppReceivedDataEventArgs ReadDataEventArgs)
{
    string message = ReadDataEventArgs.DataString;

    // 对传入的信息做些什么
    Debug.WriteLine($"Message:{message}");

    // 在这个例子中我们用OK来回应
    NordicSpp spp = sender as NordicSpp;
    spp.SendString("OK");
}

处理连接事件

当客户端连接或断开与SPP服务器的连接时将引发连接事件。

在这里,当客户机连接时,我们发送一条消息。

private void Spp_ConnectedEvent(IBluetoothSpp sender, EventArgs e)
{
    NordicSpp spp = sender as NordicSpp;

    if (spp.IsConnected)
    {
        spp.SendString("欢迎来到nanoFramework");
    }
}

反馈和文档

关于文档,提供反馈,问题和找出如何贡献,请参考 首页这里.

加入我们的Discord社区在这里.

Credits

这个项目的贡献者名单可以在贡献者.

许可证

nanoFramework 类库是根据 MIT license.

行为准则

本项目采用了《贡献者盟约》所规定的行为准则,以澄清我们社区的预期行为。 有关更多信息,请参阅 .NET基金会行为准则.

.NET基金会

这个项目是由 .NET基金会.