{"id":19244387,"url":"https://github.com/nanoframework/nanoframework.device.bluetooth","last_synced_at":"2025-04-21T09:33:37.509Z","repository":{"id":38211758,"uuid":"420537078","full_name":"nanoframework/nanoFramework.Device.Bluetooth","owner":"nanoframework","description":":package: nanoFramework.Device.Bluetooth class library for .NET nanoFramework","archived":false,"fork":false,"pushed_at":"2025-04-17T20:17:09.000Z","size":330,"stargazers_count":24,"open_issues_count":0,"forks_count":11,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-19T17:14:51.495Z","etag":null,"topics":["ble","bluetooth","bluetooth-low-energy","esp32","hacktoberfest","nanoframework"],"latest_commit_sha":null,"homepage":"https://www.nanoframework.net","language":"C#","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nanoframework.png","metadata":{"funding":{"open_collective":"nanoframework","github":"nanoframework"},"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-10-23T22:33:00.000Z","updated_at":"2025-04-17T20:17:11.000Z","dependencies_parsed_at":"2023-02-04T14:02:30.740Z","dependency_job_id":"a1bfbcce-4e35-4dec-b73d-b3333690f3a7","html_url":"https://github.com/nanoframework/nanoFramework.Device.Bluetooth","commit_stats":{"total_commits":112,"total_committers":9,"mean_commits":"12.444444444444445","dds":0.4107142857142857,"last_synced_commit":"1e1bd713c7acbfd4bed4a9cc6001deb6bebbb311"},"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanoframework%2FnanoFramework.Device.Bluetooth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanoframework%2FnanoFramework.Device.Bluetooth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanoframework%2FnanoFramework.Device.Bluetooth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nanoframework%2FnanoFramework.Device.Bluetooth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nanoframework","download_url":"https://codeload.github.com/nanoframework/nanoFramework.Device.Bluetooth/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250032502,"owners_count":21363850,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ble","bluetooth","bluetooth-low-energy","esp32","hacktoberfest","nanoframework"],"created_at":"2024-11-09T17:23:12.189Z","updated_at":"2025-04-21T09:33:37.495Z","avatar_url":"https://github.com/nanoframework.png","language":"C#","funding_links":["https://opencollective.com/nanoframework","https://github.com/sponsors/nanoframework"],"categories":[],"sub_categories":[],"readme":"[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=nanoframework_nanoFramework.Device.Bluetooth\u0026metric=alert_status)](https://sonarcloud.io/dashboard?id=nanoframework_nanoFramework.Device.Bluetooth) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=nanoframework_nanoFramework.Device.Bluetooth\u0026metric=reliability_rating)](https://sonarcloud.io/dashboard?id=nanoframework_nanoFramework.Device.Bluetooth) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![NuGet](https://img.shields.io/nuget/dt/nanoFramework.Device.Bluetooth.svg?label=NuGet\u0026style=flat\u0026logo=nuget)](https://www.nuget.org/packages/nanoFramework.Device.Bluetooth/) [![#yourfirstpr](https://img.shields.io/badge/first--timers--only-friendly-blue.svg)](https://github.com/nanoframework/Home/blob/main/CONTRIBUTING.md) [![Discord](https://img.shields.io/discord/478725473862549535.svg?logo=discord\u0026logoColor=white\u0026label=Discord\u0026color=7289DA)](https://discord.gg/gCyBu8T)\n\n![nanoFramework logo](https://raw.githubusercontent.com/nanoframework/Home/main/resources/logo/nanoFramework-repo-logo.png)\n\n-----\ndocument language: [English](README.md) | [简体中文](README.zh-cn.md)\n\n# Welcome to the .NET **nanoFramework** nanoFramework.Device.Bluetooth Library repository\n\n## Build status\n\n| Component | Build Status | NuGet Package |\n|:-|---|---|\n| nanoFramework.Device.Bluetooth | [![Build Status](https://dev.azure.com/nanoframework/nanoFramework.Device.Bluetooth/_apis/build/status/nanoFramework.Device.Bluetooth?repoName=nanoframework%2FnanoFramework.Device.Bluetooth\u0026branchName=main)](https://dev.azure.com/nanoframework/nanoFramework.Device.Bluetooth/_build/latest?definitionId=85\u0026repoName=nanoframework%2FnanoFramework.Device.Bluetooth\u0026branchName=main) | [![NuGet](https://img.shields.io/nuget/v/nanoFramework.Device.Bluetooth.svg?label=NuGet\u0026style=flat\u0026logo=nuget)](https://www.nuget.org/packages/nanoFramework.Device.Bluetooth/) |\n\n## nanoFramework.Device.Bluetooth class Library\n\nBluetooth Low Energy library.\n\nThis library is based on the Windows.Devices.Bluetooth UWP class library but simplified and with the asynchronous related calls made synchronous.\nThe original .Net assembly depended on Windows.Storage.Streams for DataReader \u0026 DataWriter; this library has simplified inbuilt versions.\nSo references to IBuffer in .Net UWP examples should now use Buffer instead.\n\n## Firmware versions\n\nBluetooth is currently only supported on ESP32 devices with following firmware.\n\n- ESP32_BLE_REV0\n- ESP32_BLE_REV3\n- ESP32_PSRAM_BLE_GenericGraphic_REV3\n- ESP32_S3_BLE\n- M5Core2\n- LilygoTWatch2021\n- ESP32_ETHERNET_KIT_1.2\n\nThe Bluetooth is not in every firmware due to a restriction in the IRAM memory space in the firmware image. \nFor earlier revision 1 ESP32 devices, the PSRAM implementation required a large number of PSRAM library fixes which greatly reduces the \navailable space in the IRAM area, so PSRAM is currently disabled for ESP32_BLE_REV0. With the revision 3 devices the Bluetooth and \nPSRAM are both available.\n\n## Samples\n\nA number of Bluetooth LE samples are available in the [nanoFramework samples repo](https://github.com/nanoframework/Samples/)\n\n- [Bluetooth Low energy sample 1 (Basic Read/Write/Notify)](https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/BluetoothLESample1)\n- [Bluetooth Low energy sample 2 (Add Security)](https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/BluetoothLESample2)\n- [Bluetooth Low energy sample 3 (Show cases adding or replacing some standard services)](https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/BluetoothLESample3)\n- [Bluetooth Low energy serial (SPP)](https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/BluetoothLESerial) \n- [Bluetooth Low energy central 1 (Simple Bluetooth scanner) ](https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/Central1) \n- [Bluetooth Low energy central 2 (Data collector) ](https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/Central2) \n- [Bluetooth Low energy central 3 (Authenticated Pairing sample) ](https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/Central3) \n\n## Usage\n\n### Overview\n\nThis implementation supports a cut down version of the Gatt Server and Gatt Client implementations.\n\nThe device can either run as a Server or Client, but not at the same time.  \n\nFor more information see relevant sections: -\n\n- [Gatt Server](#gatt-server)\n- [Gatt Client / Central](#gatt-client-central)\n- [Advertisement Publishing](#advertisement-publishing)\n\nAlso as part of this assembly is the NordicSPP class which implements a Serial Protocol Profile based on \nthe Nordic specification. This allows clients to easily connect via Bluetooth LE to send and receive messages via a \nBluetooth Serial Terminal application. A common use case is for provisioning devices.  See SPP section later for usage. \n\n### Attributes and UUIDs\n\nEach service, characteristic and descriptor is defined by it's own unique 128-bit UUID. These are \ncalled GUID in .Net and UUID in the Bluetooth specifications. \n\nIf the attribute is standard UUID defined by the Bluetooth SIG, it will also have a corresponding 16-bit short ID (for example, \nthe characteristic **Battery Level** has a UUID of 00002A19-0000-1000-8000-00805F9B34FB and the short ID is 0x2A19). \nThe common standard UUIDs can be seen in the classes GattServiceUuids and GattCharacteristicUuids.\n\nIf the short ID is not present in GattServiceUuids or GattCharacteristicUuids then create your own short GUID by \ncalling the utility function CreateUuidFromShortCode.\n\n```csharp\nGuid uuid1 = Utility.CreateUuidFromShortCode(0x2A19);\n```\n\n### Security and Pairing\n\nThe assembly supports pairing with encryption and authentication.\n\nIf you don't do anything in your code it will use the **Just works** method of pairing which will enable encryption on the connection.\n\nTo enable **Authentication** you will need to handle the pairing events in your code. \n\nFor more information see [**Pairing**](#pairing) section.\n\n## Gatt Server\n\nThe main object for the Gatt Server is the **BluetoothLEServer** class. This is a singleton class so there can only be one occurance of the BluetoothLEServer object.\nThe BluetoothLEServer object can be accessed by using the static **BluetoothLEServer.Instance** property. The first call to this will create the object.\nCalling dispose will remove the object from memory.\n\nThis object is new and doesn't exist in the normal Windows implementation and was added to allow better handling of pairing and connections from the managed code.\n\n### Setting the Device Name and it Appearance\n\nThe device name and appearance is part of the Generic Access service which is automatically\nincluded in each Gatt Server definition. The name should be the name of current device and the appearance which\nis optional is a 16 bit code that represents the use of the device. Defaults to 0 which is \"Unknown Device\".\nFor details on codes see the Bluetooth Sig Assigned numbers document; section **2.6.3 Appearance Sub­category values**. Use the values column for the code.\nFor example code 0x0481 is for a cycling computer.\n\n```csharp\n    BluetoothLEServer server = BluetoothLEServer.Instance;\n    server.DeviceName = \"Esp32_01\";\n    server.Appearance = 0x0481;\n```\n\n### Defining the service and associated Characteristics\n\nThe GattServiceProvider is used to create and advertise the primary service definitions. An extra device information service will be automatically created when first service is created.\n\n```csharp\nGattServiceProviderResult result = GattServiceProvider.Create(uuid);\nif (result.Error != BluetoothError.Success)\n{\n    return result.Error;\n}\n\nserviceProvider = result.ServiceProvider;\n```\n\nTo create further services for the Gatt server call **GattServiceProvider.Create(UUID)** for each new service. \n\nAccess all created services via the BluetoothLEServer instance.\n\n```csharp\nBluetoothLEServer server = BluetoothLEServer.instance;\nGattServiceProvider[] services = server.Services()\n```\n\nOr find a specific service by using its UUID.\n\n```csharp\nBluetoothLEServer server = BluetoothLEServer.instance;\nGattServiceProvider service = server.GetServiceByUUID(uuid)\n```\n\nUsing the *Service* property of the GattServiceProvider all the required characteristics can be added. \nCurrently only Read, Write, WriteWithoutResponse, Notify and Indicate characteristics are supported.\n\n### Adding a Read Characteristic\n\nIf a userDescription is added to the GattLocalCharacteristicParameters then a user description descriptor will be automatically added to the Characteristic. \nFor a read Characteristic you will need an associated event handler to provide the data for the read.\n\n```csharp\nGattLocalCharacteristicParameters ReadParameters = new GattLocalCharacteristicParameters\n{\n    CharacteristicProperties = (GattCharacteristicProperties.Read),\n    UserDescription = \"My Read Characteristic\"\n};\n\nGattLocalCharacteristicResult characteristicResult = serviceProvider.Service.CreateCharacteristic(uuid1, ReadParameters);\nif (characteristicResult.Error != BluetoothError.Success)\n{\n    // An error occurred.\n    return characteristicResult.Error;\n}\n\n_readCharacteristic = characteristicResult.Characteristic;\n_readCharacteristic.ReadRequested += _readCharacteristic_ReadRequested;\n```\n\nYou can have a read Characteristics with a constant value by setting the **StaticValue** property.\n\n```csharp\n// Setting a Int 16 constant value to the characteristic. \nDataWriter dr = new DataWriter();\ndr.WriteInt16(123);\n\nGattLocalCharacteristicParameters ReadParameters = new GattLocalCharacteristicParameters\n{\n    CharacteristicProperties = (GattCharacteristicProperties.Read),\n    UserDescription = \"My Read Characteristic\",\n    StaticValue = dr.DetachBuffer()\n};\n\n```\nIf the **StaticValue** is set the read event will not be called and doesn't need to be defined.\n\n### Adding a Write or WriteWithoutResponse Characteristic\n\nThe Write Characteristic is used for receiving data from the client.  \n\n```csharp\nGattLocalCharacteristicParameters WriteParameters = new GattLocalCharacteristicParameters\n{\n    CharacteristicProperties = GattCharacteristicProperties.Write,\n    UserDescription = \"My Write Characteristic\"\n};\n\n\ncharacteristicResult = serviceProvider.Service.CreateCharacteristic(uuid2, WriteParameters);\nif (characteristicResult.Error != BluetoothError.Success)\n{\n    // An error occurred.\n    return characteristicResult.Error;\n}\n_writeCharacteristic = characteristicResult.Characteristic;\n_writeCharacteristic.WriteRequested += _writeCharacteristic_WriteRequested;\n```\n\n### Adding a Notify Characteristic\n\nA notify Characteristic is used to automatically notify subscribed clients when a value has changed.\n\n```csharp\nGattLocalCharacteristicParameters NotifyParameters = new GattLocalCharacteristicParameters\n{\n    CharacteristicProperties = GattCharacteristicProperties.Notify,\n    UserDescription = \"My Notify Characteristic\"\n};\n\ncharacteristicResult = serviceProvider.Service.CreateCharacteristic(uuid3, NotifyParameters);\nif (characteristicResult.Error != BluetoothError.Success)\n{\n    // An error occurred.\n    return characteristicResult.Error;\n}\n\n_notifyCharacteristic = characteristicResult.Characteristic;\n_notifyCharacteristic.SubscribedClientsChanged += _notifyCharacteristic_SubscribedClientsChanged;\n```\n\n### Sending data to a Notify Characteristic\n\nData can be sent to subscribed clients by calling the NotifyValue method on the notify characteristic.\nExtra checks can be added to only send values if there are subscribed clients or if the values has changed\nsince last notified.\n\n```csharp\nprivate static void UpdateNotifyValue(double newValue)\n{\n    DataWriter dw = new DataWriter();\n    dw.WriteDouble(newValue);\n\n    _notifyCharacteristic.NotifyValue(dw.DetachBuffer());\n}\n```\n\n## Events\n\n### Read requested event\n\nWhen a client requests to read a characteristic, the managed event will be called assuming a static value hasn't been set.\nIf no event handler is set or you don't respond in a timely manner an Unlikely Bluetooth error will be returned to client.  \nIf reading the value from a peripheral device takes time then best to put this outside the event handler.\n\nThis show the returning of 2 values to client request. \n\n```csharp\nprivate static void _readCharacteristic_ReadRequested(GattLocalCharacteristic sender, GattReadRequestedEventArgs ReadRequestEventArgs)\n{\n    GattReadRequest request = ReadRequestEventArgs.GetRequest();\n\n    // Create DataWriter and write the data into buffer\n    DataWriter dw = new DataWriter();\n    dw.WriteInt16(1);\n    dw.WriteInt32(2);\n\n    request.RespondWithValue(dw.DetachBuffer());\n\n    // If there is some sort of error then response with an error \n    //request.RespondWithProtocolError((byte)BluetoothError.DeviceNotConnected);\n}\n```\n\n## Write requested event\n\nWhen data is sent to a write characteristic the managed event is called. If no event handler is \nset or you don't respond in a timely manner an Unlikely Bluetooth error will be returned to client.\n\nThe data received is a array of bytes and this is formatted as required by characteristic. This could be a single\nvalue of Int16, Int32, string etc. or it could be a number of different values.\n\nThis shows the reading of a single Int32 value from buffer and returns an error if the wrong number \nof bytes has been supplied.\n\n```csharp\nprivate static void _writeCharacteristic_WriteRequested(GattLocalCharacteristic sender, GattWriteRequestedEventArgs WriteRequestEventArgs)\n{\n    GattWriteRequest request = WriteRequestEventArgs.GetRequest();\n\n    // Check expected data length\n    if (request.Value.Length != 4)\n    {\n        request.RespondWithProtocolError((byte)BluetoothError.NotSupported);\n        return;\n    }\n\n    // Read data from buffer of required format\n    DataReader rdr = DataReader.FromBuffer(request.Value);\n    Int32 data = rdr.ReadInt32();\n\n    // Do something with received data\n    Debug.WriteLine($\"Rx data::{data}\");\n\n    // Respond if Write requires response\n    if (request.Option == GattWriteOption.WriteWithResponse)\n    {\n        request.Respond();\n    }\n}\n```\n## Subscribed Clients changed event\n\nFor notifiable characteristics a client can subscribe to receive the notification values. When a client\nsubscribes the managed event will be called.\nThe SubscribedClients array of the characteristics contains the connected clients.\n\n```csharp\nprivate static void _notifyCharacteristic_SubscribedClientsChanged(GattLocalCharacteristic sender, object args)\n{\n    if ( sender.SubscribedClients.Length \u003e 0)\n    {\n         Debug.WriteLine($\"Client connected \");\n    }\n}\n```\n\n## Advertising your service\n\nOnce all the Characteristics have been created you need to advertise the Service so other devices can see it \nand/or connect to it. \n\nAs part of the ServiceProvider we add the following data sections to the advertisement \npayload automatically.\n\n* Flags \n* Complete Local Name \n* UUID of service defined on provider.\n* ServiceData ( Optional )\n\nAn extension for nanoFramework allows the advertisement to be added to by adding data sections to\nthe Advertisement property of the GattServiceProviderAdvertisingParameters object.\nAlso by setting the GattServiceProviderAdvertisingParameters.CustomAdvertisement flag all data sections can\nbe set up by the user.\n\n### Starting the advertisement.\n```csharp\nserviceProvider.StartAdvertising(new GattServiceProviderAdvertisingParameters()\n{\n    IsConnectable = true,\n    IsDiscoverable = true\n});\n```\n\n# Gatt Client / Central\n\nThe Bluetooth LE client is used to look for advertisements from devices(Servers) and to connect to those \ndevices and read and/or write values to Characteristics. Notification can be set up so events will automatically\nfire when a value changes.\n\nWe have 2 samples available:\n- Central1  - This straight forward sample to just watch for advertisements and print out results.\n- Central2  - This more of a full on sample and is an example on how to collect values in this case temperatures \nfrom a bunch of devices with the Environmental Sensor service. The devices are an updated version of the Sample device example.\n\n## Watching for Advertisements\n\nTo watch for advertisements you use the BluetoothLEAdvertisementWatcher class.\n\n```csharp\n    BluetoothLEAdvertisementWatcher watcher = new();\n    watcher.Received += Watcher_Received;\n    watcher.Start();\n```\nWhen a advertisement is received an event will be raised calling the Watcher_Received event handler.\nIn the event handler you will be able to select a device using the information supplied on the event.\nThis could be device LocalName or other data supplied in the advertisement data.\n\nIf you start a Watcher to look for advertisements from Server devices you will not \nbe able to connect to those devices until the Watcher has been stopped, but you can receive data from connected \ndevices while Watcher is scanning. So the Watcher needs to be stopped while connections are being made to servers.\n\nSee samples for more information.\n\nFilters can be added to the BluetoothLEAdvertisementWatcher.\nTHere are 2 filters  available:-\n* RSSI filter - Advertisements are only received from devices within a certain signal strength. \n* Advertisement Filter - Advertisements are filtered by the contents of the advertisement.\n\n### RSSI filter\n```csharp\n    watcher.SignalStrengthFilter.InRangeThresholdInDBm = -70;\n    watcher.SignalStrengthFilter.OutOfRangeThresholdInDBm = -77;\n    watcher.SignalStrengthFilter.OutOfRangeTimeout = TimeSpan.FromMilliseconds(10000);\n```\n\n### Advertisements Filter\n\nAdvertisements are only received from devices which match the data sections or part of a data section contained in the filter.\n\nUpdate the advertisement object with data sections for the filter to match against.\nThere are some properties on object for common data sections. For other data sections load the \"dataSections\" arrayList property with the data sections required to match.\n\nUsing a property to match a local name of device.\nOnly adverts from devices with a local name of \"Sample\" will be received.\n```csharp\n    watcher.AdvertisementFilter.Advertisement.LocalName = \"Sample\";\n```\n\nThis filter uses a partial match of part of data section.\nSelect all Advertisements with an \"a\" in 2nd position of Local name\n\nIf you want to filter on part of a data section then use the BytePatterns arrayList.\n```csharp\n   BluetoothLEAdvertisementBytePattern pattern = new BluetoothLEAdvertisementBytePattern()\n   {\n      DataType = (byte)BluetoothLEAdvertisementDataSectionType.CompleteLocalName,\n      Data = new Buffer(new Byte[] { (Byte)'a' }),\n      Offset = 1\n   };\n\n   watcher.AdvertisementFilter.BytePatterns.Add(pattern2;\n```\n\nAny data thats contained in an advertisement can be filtered on.\nAdvertisement and byte patterns can be used together.\n\nFor more examples of advertisement filters see the Watcher filter sample.\n\n## Creating a device and connecting to device.\n\nTo communicate with a device a BluetoothLEDevice class needs to be created using the devices Bluetooth address and type.\nThis can be the Bluetooth address from the BluetoothLEAdvertisementWatcher event or using a hard coded address.\n\nIn this case from the Watcher advertisement received event BluetoothAddress argument.\n```csharp\nBluetoothLEDevice device = BluetoothLEDevice.FromBluetoothAddress(args.BluetoothAddress)\n```\nThere are no specific connection methods, a connection will be made automatically when the device is queried for services or a Pairing operation is started. \nThe ConnectionStatusChanged event can be used to detect a change in connection status and an attempt to \nreconnect can be done by a query to the devices services again. Avoid doing this in the event, as it can block other \nevents being fired during the connection.\n\nAfter connecting to the device go back to Watching for advertisements with the restriction that you can't connect to newly found devices until the Watching is stopped.\nYou can still communication with connected devices while the Watcher is running. Best way is to collect all found devices in a table until Watcher is\nstopped then connect to all found devices. See [Central 2 sample](https://github.com/nanoframework/Samples/tree/main/samples/Bluetooth/Central2)\n\nThe Close() method is not exposed in the desktop version but it has been implemented in this version \nto give better control over the connection.\n\n## Getting information about the connected device\n\nThe Generic Access device name and appearance code are available as properties of the BluetoothLEDevice object after connecting to the device.\n\n## Querying device services\n\nQuerying for all services provided by device. If the GattDeviceServicesResult status is a GattCommunicationStatus.Success then an\narray of GattDeviceService objects are available in the GattDeviceServicesResult Services property.\n```csharp\n    GattDeviceServicesResult sr = device.GetGattServices();\n    if (sr.Status == GattCommunicationStatus.Success)\n    {\n        GattDeviceService[] services = sr.Services;\n\n    }\n```\nQuerying for a specific service provided by device.\n```csharp\n    GattDeviceServicesResult sr = device.GetGattServicesForUuid(GattServiceUuids.EnvironmentalSensing);\n    if (sr.Status == GattCommunicationStatus.Success)\n    {\n        \n    }\n```\nBoth above get service methods will try to connect to device.\n\nNote: \nThe services are cached so the first time they are queried it will retrieve them from the device.\nFurther calls for services will return the cached results. To clear the cache the device must be disposed.\n\n\n## Querying service characteristics\n\nWith the GattDeviceService object a query can be made for the required Characteristics.\nIn the same way as the service there is a method to query for a all Characteristics or just specific Characteristics.\n\nQuery for all Characteristics\n```csharp\n    GattCharacteristicsResult cr = service.GetCharacteristics();\n    if (cr.Status == GattCommunicationStatus.Success)\n    {\n        GattCharacteristic[] chars = cr.Characteristics;\n    }    \n```\n\nQuery for service for all Characteristics with the standard Temperature UUID.\n```csharp\n    GattCharacteristicsResult cr = service.GetCharacteristicsForUuid(GattCharacteristicUuids.Temperature);\n    if (cr.Status == GattCommunicationStatus.Success)\n    {\n        GattCharacteristic[] gcs = cr.Characteristics;\n    }    \n```\nNote: \nThe Characteristics are cached so the first time requested it will retrieve them from the device.\nFurther calls to same service will return the cached results.\n\n## Querying characteristics descriptors\n\nDescriptors can be retrieved in the same way as Services and Characteristics using the methods \nGetDescriptors and GetDescriptorsForUuid.\n\nIn these examples gc is the GattCharacteristic object.\n\nGet all descriptors.\n```csharp\n    GattDescriptorsResult dr = gc.GetDescriptors();\n    if (dr.Status == GattCommunicationStatus.Success)\n    {\n\n    }   \n```\nGet all descriptors with particular UUID.\n```csharp\n    GattDescriptorsResult dr = gc.GetDescriptorsForUuid(uuid);\n    if (dr.Status == GattCommunicationStatus.Success)\n    {\n\n    }   \n```\nThe properties **UserDescription** or **PresentationFormats** will automatically retrieve the descriptors\nfrom the device. Any further calls to get descriptors will come from a local cache.\n\n## Reading and Writing attribute values\n\nTo read a value from a Characteristic or Descriptor use the ReadValue() method. If successful a Buffer object\nwill be available where the data can be read from using the DataReader.\n\nThe format of the data in Buffer will depend on Characteristic/Descriptors being read.\n\nThis example reads the value from Characteristic/Descriptor and loads the 3 bytes into a Byte and a ushort.\n```csharp\n    GattReadResult rr = gc.ReadValue();\n    if (rr.Status == GattCommunicationStatus.Success)\n    {\n        DataReader rdr = DataReader.FromBuffer(rr.Value);\n        Byte data1 = rdr.ReadByte();\n        ushort data2 = rdr.ReadInt16();\n\n    }\n```\n\nTo write to a Characteristic or Descriptor, create a Buffer with desired data and call the WriteValueWithResult() method.\nThis can be used to Write with or without a response.\n```csharp\n    DataWriter dw = new();\n    dw.WriteBytes(new byte[] { 1, 2, 3, 4 });\n    dw.WriteUInt32(23);\n\n    GattWriteResult wr = gc.WriteValueWithResult(dw.DetachBuffer(), , GattWriteOption.WriteWithResponse);\n    if (wr.Status == GattCommunicationStatus.Success)\n    {\n\n    }\n```\n                       \n## Enabling the value changed notifications\n\nThis enables the receiving of events when a Characteristic value changes on the server. The notifications are enabled\nby setting up an event then setting the value of the CCCD descriptor as below.\n\nExample with gc being the GattCharacteristic to enable.\n```csharp\n    // Set up a notify value changed event\n    gc.ValueChanged += ValueChanged;\n\n    // and configure CCCD for Notify\n    gc.WriteClientCharacteristicConfigurationDescriptorWithResult(GattClientCharacteristicConfigurationDescriptorValue.Notify);\n```\nTo switch off the notifications write the none value to the CCCD descriptor.\n\n## Event handler for notification events. \n\nThe sender is the GattCharacteristic the change is coming from and the valueChangedEventArgs.CharacteristicValue is\nthe Buffer value with the new value. \n```csharp\n    private static void ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs valueChangedEventArgs)\n    {\n        DataReader rdr = DataReader.FromBuffer(valueChangedEventArgs.CharacteristicValue);\n\n        // Read value from DataReader\n    }\n```\n\n## Handling device errors\n\nHandle connection errors by monitoring the ConnectionStatusChanged event on the BluetoothLEDevice object.  \nIf the connection is lost, reconnect by requesting the services again. \n\nSee Central2 sample.\n\nMake sure you check the return status for all requests to make sure they are successful.\n\n\n# Pairing\n\nPairing is handled by the **DevicePairing** class which is a property on the *BluetoothLEServer* and *BluetoothLEDevice* classes.\nThe properties *ProtectionLevel* and *IOCapabilities* control how the pairing will be done.\n\nFor good information on Bluetooth Pairing and how the IO Capabilities effect the type of pairing see this very useful [Blog](https://www.bluetooth.com/blog/bluetooth-pairing-part-2-key-generation-methods/) on www.bluetooth.com\n\n## ProtectionLevel\n\nBy default the *ProtectionLevel* is **None** and the *IOCapabilities* is **NoInputNoOutput** so the pairing will use **Just Works** method if both ends are nanoFramework.\n\nThe ProtectionLevel will be automatically updated depending on the requirements of any added Characteristics.\nThis can be manually set to force a different level of protection.\n\n| *ProtectionLevel* | |\n| ------- | ----- |\n| None | No security is used for connection, legacy pairing |\n| Encryption | A Secure connection is used. Keys are automatically generated for connection. | \n| EncryptionAndAuthentication | As secure connection is used and authentication is required. IoCapabilities must be set correctly |\n\n## IOCapabilities\n\nThe IO Capabilities are input and output peripherals available on the device for pairing.\nDepending on what IOCapabilities each device has will govern how the pairing will be done.\n\n| *IOCapabilities* | |\n| ------- | ----- |\n| DisplayOnly | Only a Display is available. This can be used to display passKey for other device to input. |\n| DisplayYesNo | A Display and a means of inputing Yes or No. i.e 2 buttons. | \n| NoInputNoOutput | No input or output is available. |\n| KeyboardDisplay | A display and Keyboard is available |\n\n## Pairing Matrix\n\nThe Initiator is the device thats starts the pairing operation. This would normally be the Client.\nThe Responder is the device responding to the pairing request.\n\n*Just Works*  : Means there is no input required and connection is just set up. If any Characteristics \nhave a protection level of Authentication then a access error will be given.\n\n| **Responder IOCapabilities** || \u003c- | \u003c- | *Initiator* *IOCapabilities* | -\u003e  | -\u003e |\n| --------- | ----------- | ------------ | ------------ | --------------- | --------------- |\n|  || *DisplayOnly* | *DisplayYesNo* | *KeyboardOnly* | *NoInputNoOutput* | *KeyboardDisplay* | \n| **DisplayOnly** || Just Works | Just Works | PassKey R-\u003eI | Just Works | PassKey R-\u003eI |\n| **DisplayYesNo** || Just Works | Just Works or Compare(sec) | PassKey R-\u003eI | Just Works | PassKey R-\u003eI or Compare(sec) |\n| **KeyboardOnly** || PassKey I-\u003eR | PassKey I-\u003eR | Passkey R\u0026I | Just Works | PassKey I-\u003eR |\n| **NoInputNoOutput** || Just Works | Just Works | Just Works | Just Works | Just Works |\n| **KeyboardDisplay** || PassKey I-\u003eR | PassKey I-\u003eR or Compare(sec) | PassKey R-\u003eI | Just Works | PassKey I-\u003eR or Compare(sec) |\n\nWhere:-\n* *Passkey R-\u003eI* : The passkey is displayed on responder and input done on initiator.\n* *Passkey I-\u003eR* : The passkey is displayed on initiator and input done on responder.\n* *Passkey R\u0026I*  : PassKey is inputted on initiator and responder.\n* *Compare(sec)* : Numeric comparison when using secure connection. Both ends input and display passkey.\n\n### Common use cases for IOCapabilities.\n\n| Responder(Client) | Pairing | Initiator(Server) |\n| --------- | ----------- | ------------ |\n| *NoInputNoOutput* | Just Works | *NoInputNoOutput* |\n| *KeyboardOnly* | PassKey input/sent from Client, Server checks | *DisplayOnly* |\n\n### Code examples \n\nFor a full example see the samples *BluetoothLESample2* and *Central3*. \n\n#### Server\n\n##### Setup for Server pairing. \n\nThe client has to provide the correct passKey.\n\n```csharp\n    BluetoothLEServer server = BluetoothLEServer.Instance;\n    server.DeviceName = \"MyTestEsp32\";\n\n    // Set up an event handler for handling pairing requests\n    server.Pairing.PairingRequested += Pairing_PairingRequested;\n    server.Pairing.PairingComplete += Pairing_PairingComplete;\n\n    // Say we have a display\n    server.Pairing.IOCapabilities = DevicePairingIOCapabilities.DisplayOnly;\n\n    // Set ProtectionLevel\n    server.Pairing.ProtectionLevel = DevicePairingProtectionLevel.EncryptionAndAuthentication;\n\n    // Start the Bluetooth server. \n    server.Start();\n\n    // Add services and advertise\n\n```\n##### Event Handler for PairingRequested event. \n\nThe PairingKind indicates what the application needs to do.\nIn this case its DevicePairingKinds.DisplayPin which just needs the passKey to compare with client.\n\nFor a general use device with a display the passkey should be displayed so client knows what to input.\n\n```csharp\nprivate static void Pairing_PairingRequested(object sender, DevicePairingRequestedEventArgs args)\n{\n    switch (args.PairingKind)\n    {\n        // Passkey displayed on current device or just a know secret passkey\n        // Tell BLE what passkey is, so it can be checked against what has been entered on other device.\n        case DevicePairingKinds.DisplayPin:\n            args.Accept(654321);\n            break;\n    }\n}\n```\n\n##### Event handle for PairingComplete event. \n\nThis will inform program if pairing was successful or not.\nCheck the args.Status is a success and the IsPaired property is true.\n\n```csharp\nprivate static void Pairing_PairingComplete(object sender, DevicePairingEventArgs args)\n{\n    DevicePairing dp = sender as DevicePairing;\n         \n    Console.WriteLine($\"PairingComplete:{args.Status} IOCaps:{dp.IOCapabilities} IsPaired:{dp.IsPaired} IsAuthenticated:{dp.IsAuthenticated}\");\n}\n```\n\n\n#### Client\n\n##### Setup of BluetoothLEDevice for pairing with Authentication.\n\n```csharp\nstatic void SetupDevice(BluetoothLEDevice device)\n{\n    // Event handlers pairing\n    device.Pairing.PairingRequested += Pairing_PairingRequested;\n    device.Pairing.PairingComplete += Pairing_PairingComplete;\n\n    // Set up IOCapabilities \u0026 ProtectionLevel\n    device.Pairing.IOCapabilities = DevicePairingIOCapabilities.KeyboardOnly;\n    device.Pairing.ProtectionLevel = DevicePairingProtectionLevel.EncryptionAndAuthentication;\n\n    // Pair with device\n    DevicePairingResult pairResult = device.Pairing.Pair();\n\n```\n\n##### Event handler for inputting passkey. \n\n```csharp\nprivate static void Pairing_PairingRequested(object sender, DevicePairingRequestedEventArgs args)\n{\n    Console.WriteLine($\"Pairing_PairingRequested:{args.PairingKind}\");\n\n    switch (args.PairingKind)\n    {\n        case DevicePairingKinds.ProvidePin:\n            // Provide valid passkey\n            args.Accept(654321);\n            break;\n    }\n}\n\n```\n\n##### Event Handler for pairing complete event.\n\n```csharp\nprivate static void Pairing_PairingComplete(object sender, DevicePairingEventArgs args)\n{\n    // Pick up DevicePairing from sender or just use it directly\n    DevicePairing pairing = (DevicePairing)sender;\n\n    if (args.Status == DevicePairingResultStatus.Paired)\n    {\n        Console.WriteLine($\"PairingComplete:{args.Status} IOCaps:{pairing.IOCapabilities} IsPaired:{pairing.IsPaired} IsAuthenticated:{pairing.IsAuthenticated}\");\n    }\n    else\n    {\n        Console.WriteLine($\"PairingComplete failed - status = {args.Status}\");\n    }\n}\n```\n\n# Advertisement Publishing\n\nThe BluetoothLEAdvertisementPublisher class allows the configuration and advertising of a Bluetooth LE advertisement packet.\nThe payload of the advertisement is configured when the class is constructed via the BluetoothLEAdvertisement class.\n\nThe BluetoothLEAdvertisement class is used to control exactly what the advertisement packet contains and is mainly\nused to create beacons.\n\nThe advertisement is constructed by adding BluetoothLEAdvertisementDataSection to the BluetoothLEAdvertisement class.\nFor some common Data Sections there are properties on the BluetoothLEAdvertisement class which automatically add the correct \ndata section to advertisement. i.e. LocalName, Flags\n\nOnce the BluetoothLEAdvertisementPublisher class has been constructed the advertising can be started or stopped with \nthe Start() and Stop() methods.\n\n## Creating an advertisement packet\nFor legacy advertisements the packet length is 31 bytes which includes all data sections.\nEach data section is 1 byte for length, 1 byte for section type and the data bytes. Any data section that\ndoesn't fit in advertisement will be moved to the scan response or left off it no room.\n\nCurrently we don't support extended advertisement. This will be available in future release.\n\n\n```csharp\nBluetoothLEAdvertisementPublisher publisher = new BluetoothLEAdvertisementPublisher();\n\n// Add Flags using property\npublisher.Advertisement.Flags = BluetoothLEAdvertisementFlags.GeneralDiscoverableMode |\n                                BluetoothLEAdvertisementFlags.DualModeControllerCapable |\n                                BluetoothLEAdvertisementFlags.DualModeHostCapable;\n\n// Adding flags using Data Sections\npublisher.Advertisement.\n\n```\n\n\n## Starting and Stopping Publisher\nAdvertise for 1 minute.\n```csharp\npublisher.Start();\n\nThread.Sleep(60000);\n\npublisher.Stop();\n```\n\n\n\n\n---\n\n# Bluetooth Serial Port Profile(SPP)\n\nThis assembly has an implementation of the Nordic SPP which can easily be used to send messages between a Bluetooth client and the device \nrunning the SPP. This is a simple way of provisioning a device with any extra information like WiFi details.\n\nThere are a number of Android and IOS app that support Nordic SPP that can be used to send/receive messages.\n\n## Create instance of the SPP\n\nCreate an instance of the SPP and provide event handlers for reading messages and client connection activity.\nStart advertising with a device name.\n\nUses namespace **nanoFramework.Device.Bluetooth.Spp**\n\n```csharp\nNordicSpp spp = new NordicSpp();\nspp.ReceivedData += Spp_ReceivedData;\nspp.ConnectedEvent += Spp_ConnectedEvent;\n\nspp.Start(\"MySpp\");\n\n```\n\nWhen complete, call the Stop method to stop the SPP.\n\n## Handling Read Data events\n\nData can be read as either a array of bytes or as a string.\n\n```csharp\nprivate void Spp_ReceivedData(IBluetoothSpp sender, SppReceivedDataEventArgs ReadDataEventArgs)\n{\n    string message = ReadDataEventArgs.DataString;\n\n    // Do something with incoming message\n    Debug.WriteLine($\"Message:{message}\");\n\n    // For this example lets respond with \"OK\"\n    NordicSpp spp = sender as NordicSpp;\n    spp.SendString(\"OK\");\n}\n```\n\n## Handling connection events\n\nA connection event is thrown when a client connects or disconnects from SPP server.\nHere we send a message when a client connects. \n\n```csharp\nprivate void Spp_ConnectedEvent(IBluetoothSpp sender, EventArgs e)\n{\n    NordicSpp spp = sender as NordicSpp;\n\n    if (spp.IsConnected)\n    {\n        spp.SendString(\"Welcome to nanoFramework\");\n    }\n}\n```\n\n## Feedback and documentation\n\nFor documentation, providing feedback, issues and finding out how to contribute please refer to the [Home repo](https://github.com/nanoframework/Home).\n\nJoin our Discord community [here](https://discord.gg/gCyBu8T).\n\n## Credits\n\nThe list of contributors to this project can be found at [CONTRIBUTORS](https://github.com/nanoframework/Home/blob/main/CONTRIBUTORS.md).\n\n## License\n\nThe **nanoFramework** Class Libraries are licensed under the [MIT license](LICENSE.md).\n\n## Code of Conduct\n\nThis project has adopted the code of conduct defined by the Contributor Covenant to clarify expected behavior in our community.\nFor more information see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).\n\n## .NET Foundation\n\nThis project is supported by the [.NET Foundation](https://dotnetfoundation.org).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanoframework%2Fnanoframework.device.bluetooth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnanoframework%2Fnanoframework.device.bluetooth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnanoframework%2Fnanoframework.device.bluetooth/lists"}