From 0fb408555e46c708981619284b44ebf491983592 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Thu, 7 Mar 2024 21:11:58 -0500 Subject: [PATCH 1/5] Docs update for endpoint listener plugins --- .../MidiChannelEndpointListener.md | 33 +++++++++++++++++++ .../MidiGroupEndpointListener.md | 25 ++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/docs/developer-docs/Windows.Devices.Midi2/processing-plugins/MidiChannelEndpointListener.md b/docs/developer-docs/Windows.Devices.Midi2/processing-plugins/MidiChannelEndpointListener.md index 762aa3da..1e600be3 100644 --- a/docs/developer-docs/Windows.Devices.Midi2/processing-plugins/MidiChannelEndpointListener.md +++ b/docs/developer-docs/Windows.Devices.Midi2/processing-plugins/MidiChannelEndpointListener.md @@ -27,6 +27,39 @@ In addition to the properties and methods in `IMidiEndpointMessageProcessingPlug | ---- | ---- | | `MidiChannelEndpointListener()` | Construct a new instance of this type | +## Example + +```cpp +// set up your message receive handler and create your connection +// before setting up the individual message listeners. The event +// handler has the same signature as the main message received +// event on the connection. + +midi2::MidiChannelEndpointListener channelsListener; + +// listening to channels generally only makes sense if you also +// specify the group you are listening to. +channelsListener.IncludeGroup(midi2::MidiGroup(5)); + +// add the channels you are listening to. Any messages which do +// not have channels will not be raised through the event here. +channelsListener.IncludeChannels().Append(midi2::MidiChannel(3)); +channelsListener.IncludeChannels().Append(midi2::MidiChannel(7)); + +// set this if you don't want the main message received event on the +// connection to fire for any messages this plugin handles. +channelsListener.PreventFiringMainMessageReceivedEvent(true); + +auto channelMessagesReceivedEventToken = channelsListener.MessageReceived(MyMessageReceivedHandler); + +myConnection.AddMessageProcessingPlugin(channelsListener); + +// open after setting up the plugin so you don't miss any messages +myConnection.Open(); + +// ... +``` + ## IDL [MidiChannelEndpointListener IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiChannelEndpointListener.idl) diff --git a/docs/developer-docs/Windows.Devices.Midi2/processing-plugins/MidiGroupEndpointListener.md b/docs/developer-docs/Windows.Devices.Midi2/processing-plugins/MidiGroupEndpointListener.md index 903a41fd..00ba1e6f 100644 --- a/docs/developer-docs/Windows.Devices.Midi2/processing-plugins/MidiGroupEndpointListener.md +++ b/docs/developer-docs/Windows.Devices.Midi2/processing-plugins/MidiGroupEndpointListener.md @@ -28,6 +28,31 @@ In addition to the properties and methods in `IMidiEndpointMessageProcessingPlug | ---- | ---- | | `MidiGroupEndpointListener()` | Construct a new instance of this type | + +```cpp +// set up your message receive handler and create your connection +// before setting up the individual message listeners. The event +// handler has the same signature as the main message received +// event on the connection. + +midi2::MidiGroupEndpointListener groupsListener; +groupsListener.IncludeGroups().Append(midi2::MidiGroup(5)); +groupsListener.IncludeGroups().Append(midi2::MidiGroup(6)); + +// set this if you don't want the main message received event on the +// connection to fire for any messages this plugin handles. +groupsListener.PreventFiringMainMessageReceivedEvent(true); + +auto groupsMessagesReceivedEventToken = groupsListener.MessageReceived(MyMessageReceivedHandler); + +myConnection.AddMessageProcessingPlugin(groupsListener); + +// open after setting up the plugin so you don't miss any messages +myConnection.Open(); + +// ... +``` + ## IDL [MidiGroupEndpointListener IDL](https://github.com/microsoft/MIDI/blob/main/src/api/Client/Midi2Client/MidiGroupEndpointListener.idl) From 59008e6e486d8cf5c785551a8104c622e99ee674 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Thu, 7 Mar 2024 21:12:03 -0500 Subject: [PATCH 2/5] Update MIDI Project electron projection notes.txt --- .../MIDI Project electron projection notes.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build/electron-projection/MIDI Project electron projection notes.txt b/build/electron-projection/MIDI Project electron projection notes.txt index 86312828..6be9f291 100644 --- a/build/electron-projection/MIDI Project electron projection notes.txt +++ b/build/electron-projection/MIDI Project electron projection notes.txt @@ -34,7 +34,9 @@ copy Windows.winmd from the unionmetadata to same folder copy C:\Program Files\Microsoft Visual Studio\2022\Preview\Common7\IDE\VC\vcpackages platform.winmd to same folder -Comment out SendMessageStruct body and reference +Comment out SendSingleMessageStruct body and reference +Comment out SendMultipleMessagesStructList +Comment out SendMultipleMessagesStructArray Open binding.sln in VS and recompile in BOTH debug and release From bfe149f85c1396069668705d38b1caa943d1263b Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Thu, 7 Mar 2024 21:12:15 -0500 Subject: [PATCH 3/5] Working on BLE MIDI 1.0 prototype code --- .../ble-midi1-proto/PropertySheet.props | 16 + .../ble-midi1-proto/ble-midi1-proto.sln | 31 ++ .../ble-midi1-proto/ble-midi1-proto.vcxproj | 129 ++++++ src/prototypes/ble-midi1-proto/main.cpp | 367 ++++++++++++++++++ .../ble-midi1-proto/packages.config | 4 + src/prototypes/ble-midi1-proto/pch.cpp | 1 + src/prototypes/ble-midi1-proto/pch.h | 32 ++ 7 files changed, 580 insertions(+) create mode 100644 src/prototypes/ble-midi1-proto/PropertySheet.props create mode 100644 src/prototypes/ble-midi1-proto/ble-midi1-proto.sln create mode 100644 src/prototypes/ble-midi1-proto/ble-midi1-proto.vcxproj create mode 100644 src/prototypes/ble-midi1-proto/main.cpp create mode 100644 src/prototypes/ble-midi1-proto/packages.config create mode 100644 src/prototypes/ble-midi1-proto/pch.cpp create mode 100644 src/prototypes/ble-midi1-proto/pch.h diff --git a/src/prototypes/ble-midi1-proto/PropertySheet.props b/src/prototypes/ble-midi1-proto/PropertySheet.props new file mode 100644 index 00000000..b0c62269 --- /dev/null +++ b/src/prototypes/ble-midi1-proto/PropertySheet.props @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/src/prototypes/ble-midi1-proto/ble-midi1-proto.sln b/src/prototypes/ble-midi1-proto/ble-midi1-proto.sln new file mode 100644 index 00000000..9ba3c530 --- /dev/null +++ b/src/prototypes/ble-midi1-proto/ble-midi1-proto.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34607.79 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ble-midi1-proto", "ble-midi1-proto.vcxproj", "{4AF9289B-9B4F-4774-895E-535A72A8C285}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4AF9289B-9B4F-4774-895E-535A72A8C285}.Debug|x64.ActiveCfg = Debug|x64 + {4AF9289B-9B4F-4774-895E-535A72A8C285}.Debug|x64.Build.0 = Debug|x64 + {4AF9289B-9B4F-4774-895E-535A72A8C285}.Debug|x86.ActiveCfg = Debug|Win32 + {4AF9289B-9B4F-4774-895E-535A72A8C285}.Debug|x86.Build.0 = Debug|Win32 + {4AF9289B-9B4F-4774-895E-535A72A8C285}.Release|x64.ActiveCfg = Release|x64 + {4AF9289B-9B4F-4774-895E-535A72A8C285}.Release|x64.Build.0 = Release|x64 + {4AF9289B-9B4F-4774-895E-535A72A8C285}.Release|x86.ActiveCfg = Release|Win32 + {4AF9289B-9B4F-4774-895E-535A72A8C285}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D14FD0F4-65C4-4919-8373-EF6004DDB3E7} + EndGlobalSection +EndGlobal diff --git a/src/prototypes/ble-midi1-proto/ble-midi1-proto.vcxproj b/src/prototypes/ble-midi1-proto/ble-midi1-proto.vcxproj new file mode 100644 index 00000000..d0c8e495 --- /dev/null +++ b/src/prototypes/ble-midi1-proto/ble-midi1-proto.vcxproj @@ -0,0 +1,129 @@ + + + + + true + true + true + true + 15.0 + {4af9289b-9b4f-4774-895e-535a72a8c285} + Win32Proj + ble_midi1_proto + 10.0.22621.0 + 10.0.17134.0 + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + Application + v143 + v142 + v141 + v140 + Unicode + + + true + true + + + false + true + false + + + + + + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + _CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + Level4 + %(AdditionalOptions) /permissive- /bigobj + + + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + + + Console + false + + + + + WIN32;%(PreprocessorDefinitions) + + + + + MaxSpeed + true + true + NDEBUG;%(PreprocessorDefinitions) + stdcpp20 + stdcpp20 + + + Console + true + true + false + + + + + + + + + Create + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/prototypes/ble-midi1-proto/main.cpp b/src/prototypes/ble-midi1-proto/main.cpp new file mode 100644 index 00000000..885b32e1 --- /dev/null +++ b/src/prototypes/ble-midi1-proto/main.cpp @@ -0,0 +1,367 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + +// NOTE: This is prototype code for working through some BLE approaches before +// implementing it in the Windows service. Eventually, this code will go stale, +// but we'll keep it around. This is not production code. + +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; + +// some refs +// https://learn.microsoft.com/en-us/windows/uwp/devices-sensors/gatt-client + + + +#define MIDI_BLE_SERVICE_CHARACTERISTIC L"{03B80E5A-EDE8-4B33-A751-6CE34EC4C700}" +#define MIDI_BLE_DATA_IO_CHARACTERISTIC L"{7772E5DB-3868-4112-A1A9-F2669D106BF3}" + +winrt::guid MIDI_BLE_SERVICE_CHARACTERISTIC_GUID(MIDI_BLE_SERVICE_CHARACTERISTIC); +winrt::guid MIDI_BLE_DATA_IO_CHARACTERISTIC_GUID(MIDI_BLE_DATA_IO_CHARACTERISTIC); + +// Notes: +// Write (encryption recommended, write without response is required) +// Read (encryption recommended, respond with no payload) +// Notify (encryption recommended) +// Max connection interval is 15ms. Lower is better. + + +enumeration::DeviceWatcher m_Watcher{ nullptr }; +bool m_enumerationCompleted{ false }; + +winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Added_revoker m_DeviceAdded; +winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Removed_revoker m_DeviceRemoved; +winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Updated_revoker m_DeviceUpdated; +winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::Stopped_revoker m_DeviceStopped; +winrt::impl::consume_Windows_Devices_Enumeration_IDeviceWatcher::EnumerationCompleted_revoker m_DeviceEnumerationCompleted; + + +void OnDeviceAdded(enumeration::DeviceWatcher, enumeration::DeviceInformation info) +{ + std::cout << "Added: " << winrt::to_string(info.Id()) << std::endl; + std::cout << "Name: " << winrt::to_string(info.Name()) << std::endl; + + + // get the BLE device from this + + auto bleDevice = bt::BluetoothLEDevice::FromIdAsync(info.Id()).get(); + + if (bleDevice != nullptr) + { + std::cout << "BLE Name: " << winrt::to_string(bleDevice.Name()) << std::endl; + + if (bleDevice.ConnectionStatus() == bt::BluetoothConnectionStatus::Connected) + { + std::cout << "Current status: Connected " << std::endl; + } + else + { + std::cout << "Current status: Disconnected " << std::endl; + } + + + + auto gattServicesResult = bleDevice.GetGattServicesAsync().get(); + + if (gattServicesResult.Status() == gatt::GattCommunicationStatus::Success) + { + for (auto service : gattServicesResult.Services()) + { + auto serviceUuid = service.Uuid(); + + if (serviceUuid == MIDI_BLE_SERVICE_CHARACTERISTIC_GUID) + { + std::cout << "MIDI Service Found: " << winrt::to_string(winrt::to_hstring(serviceUuid)) << std::endl; + + auto characteristics = service.GetAllCharacteristics(); + + for (auto characteristic : characteristics) + { + auto uuid = characteristic.Uuid(); + //auto descriptorResult = characteristic.GetDescriptorsForUuidAsync(uuid).get(); + + //for (auto desc : descriptorResult.Descriptors()) + //{ + // desc. + //} + + if (uuid == MIDI_BLE_DATA_IO_CHARACTERISTIC_GUID) + { + std::cout << "MIDI Data IO Characteristic Found: " << winrt::to_string(winrt::to_hstring(uuid)) << std::endl; + + auto properties = characteristic.CharacteristicProperties(); + + if ((properties & gatt::GattCharacteristicProperties::Read) == gatt::GattCharacteristicProperties::Read) + { + // includes input port + std::cout << "- Supports READ (input port)" << std::endl; + } + + if ((properties & gatt::GattCharacteristicProperties::Write) == gatt::GattCharacteristicProperties::Write) + { + // includes output port + std::cout << "- Supports WRITE (output port)" << std::endl; + } + + if ((properties & gatt::GattCharacteristicProperties::Notify) == gatt::GattCharacteristicProperties::Notify) + { + std::cout << "- Supports NOTIFY" << std::endl; + } + + } + + + } + + // no need to continue looking + break; + } + } + } + else + { + std::cout << "Unable to communicate with device to get gatt services" << std::endl; + } + + std::cout << "------------------------" << std::endl; + + } + +} + +void OnDeviceRemoved(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate upd) +{ + std::cout << "Removed: " << upd.Id().c_str() << std::endl; +} + +void OnDeviceUpdated(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate upd) +{ + std::cout << "Updated: " << upd.Id().c_str() << std::endl; + + auto bleDevice = bt::BluetoothLEDevice::FromIdAsync(upd.Id()).get(); + + if (bleDevice != nullptr) + { + std::cout << "BLE Name: " << winrt::to_string(bleDevice.Name()) << std::endl; + + if (bleDevice.ConnectionStatus() == bt::BluetoothConnectionStatus::Connected) + { + std::cout << "Current status: Connected " << std::endl; + } + else + { + std::cout << "Current status: Disconnected " << std::endl; + } + } +} + +void OnDeviceStopped(enumeration::DeviceWatcher, foundation::IInspectable) +{ + +} + +void OnEnumerationCompleted(enumeration::DeviceWatcher, foundation::IInspectable) +{ + m_enumerationCompleted = true; + +} + +void TestEnumeration() +{ + // enumerate ble MIDI devices + + winrt::hstring deviceSelector = bt::BluetoothLEDevice::GetDeviceSelector(); + + + auto requestedProperties = winrt::single_threaded_vector( + { + L"System.DeviceInterface.Bluetooth.DeviceAddress", + L"System.DeviceInterface.Bluetooth.Flags", + L"System.DeviceInterface.Bluetooth.LastConnectedTime", + L"System.DeviceInterface.Bluetooth.Manufacturer", + L"System.DeviceInterface.Bluetooth.ModelNumber", + L"System.DeviceInterface.Bluetooth.ProductId", + L"System.DeviceInterface.Bluetooth.ProductVersion", + L"System.DeviceInterface.Bluetooth.ServiceGuid", + L"System.DeviceInterface.Bluetooth.VendorId", + L"System.DeviceInterface.Bluetooth.VendorIdSource", + L"System.Devices.Connected", + L"System.Devices.DeviceCapabilities", + L"System.Devices.DeviceCharacteristics" + } + ); + + + m_Watcher = enumeration::DeviceInformation::CreateWatcher(deviceSelector, requestedProperties); + + auto deviceAddedHandler = foundation::TypedEventHandler(OnDeviceAdded); + auto deviceRemovedHandler = foundation::TypedEventHandler(OnDeviceRemoved); + auto deviceUpdatedHandler = foundation::TypedEventHandler(OnDeviceUpdated); + auto deviceStoppedHandler = foundation::TypedEventHandler(OnDeviceStopped); + auto deviceEnumerationCompletedHandler = foundation::TypedEventHandler(OnEnumerationCompleted); + + m_DeviceAdded = m_Watcher.Added(winrt::auto_revoke, deviceAddedHandler); + m_DeviceRemoved = m_Watcher.Removed(winrt::auto_revoke, deviceRemovedHandler); + m_DeviceUpdated = m_Watcher.Updated(winrt::auto_revoke, deviceUpdatedHandler); + m_DeviceStopped = m_Watcher.Stopped(winrt::auto_revoke, deviceStoppedHandler); + m_DeviceEnumerationCompleted = m_Watcher.EnumerationCompleted(winrt::auto_revoke, deviceEnumerationCompletedHandler); + + // need to control-c to kill this for now. By design. + m_Watcher.Start(); + +} + + +IAsyncAction TestReceivingData() +{ + std::cout << "Test Receiving Data ---------------------------------------------------------" << std::endl; + + winrt::hstring id = L"BluetoothLE#BluetoothLE3c:6a:a7:f0:4e:b0-48:b6:20:1a:71:9d"; + + std::cout << "Attempting with id " << winrt::to_string(id) << std::endl; + + auto bleDevice = bt::BluetoothLEDevice::FromIdAsync(id).get(); + + if (bleDevice != nullptr) + { + std::cout << "Using device " << winrt::to_string(bleDevice.Name()) << std::endl; + + auto service = bleDevice.GetGattService(MIDI_BLE_SERVICE_CHARACTERISTIC_GUID); + + + if (service != nullptr) + { + std::cout << "Found service" << std::endl;; + + // we may need to test to see if we can open it shared instead + auto openStatus = service.OpenAsync(gatt::GattSharingMode::SharedReadAndWrite).get(); + + if (openStatus == gatt::GattOpenStatus::Success) + { + std::cout << "Service opened" << std::endl; + } + else + { + std::cout << "Unable to open service" << std::endl; + return; + } + + + // get the characteristic for MIDI + + std::cout << "Getting MIDI Data IO characteristic" << std::endl; + + auto characteristicsResult = service.GetCharacteristicsForUuidAsync(MIDI_BLE_DATA_IO_CHARACTERISTIC_GUID).get(); + + if (characteristicsResult.Status() == gatt::GattCommunicationStatus::Success) + { + if (characteristicsResult.Characteristics().Size() == 0) + { + // this is unexpected + std::cout << "Returned no characteristics for the required UUID" << std::endl; + } + else if (characteristicsResult.Characteristics().Size() > 1) + { + // TODO: Need to find out under which cases this happens + std::cout << "Returned more than one characteristic for the same UUID" << std::endl; + } + else + { + // exactly one characteristic returned + std::cout << "Returned single characteristic for the required UUID" << std::endl; + + // create the session + gatt::GattSession session{ nullptr }; + + try + { + std::cout << "Creating session" << std::endl; + + session = co_await gatt::GattSession::FromDeviceIdAsync(bleDevice.BluetoothDeviceId()); + + //session = sessionResult.get(); + } + catch (winrt::hresult_error err) + { + // Important: using .get() means we don't actually get to handle these exceptions. + + if (err.code() == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION)) + { + std::cout << "Cannot access the BLE device because it's being used by another process" << std::endl; + } + else + { + std::cout << "HRESULT exception 0x" << std::hex << err.code() << std::endl; + } + + co_return; + } + catch (...) + { + std::cout << "Exception" << std::endl; + + co_return; + } + + session.MaintainConnection(true); + + // wait for incoming data indefinitely + + auto characteristic = characteristicsResult.Characteristics().GetAt(0); + + auto readResult = characteristic.ReadValueAsync().get(); + + if (readResult.Status() == gatt::GattCommunicationStatus::Success) + { + // we get an IBuffer here + + for (int i = 0; i < readResult.Value().Length(); i++) + { + // read next byte + auto b = *(readResult.Value().data() + i); + + std::cout << std::hex << b << " "; + } + + std::cout << std::endl; + + } + session.Close(); + + } + } + else + { + std::cout << "Unable to get characteristics" << std::endl; + } + + + } + else + { + std::cout << "Unable to get service" << std::endl; + } + } + +} + +int main() +{ + init_apartment(); + +// TestEnumeration(); + TestReceivingData().get(); + + //system("pause > nul"); + system("pause"); + + +} + diff --git a/src/prototypes/ble-midi1-proto/packages.config b/src/prototypes/ble-midi1-proto/packages.config new file mode 100644 index 00000000..cbf6205e --- /dev/null +++ b/src/prototypes/ble-midi1-proto/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/prototypes/ble-midi1-proto/pch.cpp b/src/prototypes/ble-midi1-proto/pch.cpp new file mode 100644 index 00000000..bcb5590b --- /dev/null +++ b/src/prototypes/ble-midi1-proto/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/prototypes/ble-midi1-proto/pch.h b/src/prototypes/ble-midi1-proto/pch.h new file mode 100644 index 00000000..a4f032cf --- /dev/null +++ b/src/prototypes/ble-midi1-proto/pch.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License +// ============================================================================ +// This is part of the Windows MIDI Services App API and should be used +// in your Windows application via an official binary distribution. +// Further information: https://github.com/microsoft/MIDI/ +// ============================================================================ + + +#pragma once + + +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace json = ::winrt::Windows::Data::Json; +namespace enumeration = ::winrt::Windows::Devices::Enumeration; +namespace foundation = ::winrt::Windows::Foundation; +namespace collections = ::winrt::Windows::Foundation::Collections; +namespace bt = ::winrt::Windows::Devices::Bluetooth; +namespace gatt = ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile; +namespace streams = ::winrt::Windows::Storage::Streams; + +#include \ No newline at end of file From 0f66a25d6171ee028627f61fd8fc9c37c6659c36 Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Fri, 8 Mar 2024 00:58:06 -0500 Subject: [PATCH 4/5] BLE prototype code now receiving incoming data --- src/prototypes/ble-midi1-proto/main.cpp | 185 +++++++++++++++++++++++- src/prototypes/ble-midi1-proto/pch.h | 1 + 2 files changed, 178 insertions(+), 8 deletions(-) diff --git a/src/prototypes/ble-midi1-proto/main.cpp b/src/prototypes/ble-midi1-proto/main.cpp index 885b32e1..e222ceef 100644 --- a/src/prototypes/ble-midi1-proto/main.cpp +++ b/src/prototypes/ble-midi1-proto/main.cpp @@ -20,11 +20,11 @@ using namespace Windows::Foundation; -#define MIDI_BLE_SERVICE_CHARACTERISTIC L"{03B80E5A-EDE8-4B33-A751-6CE34EC4C700}" +#define MIDI_BLE_SERVICE L"{03B80E5A-EDE8-4B33-A751-6CE34EC4C700}" #define MIDI_BLE_DATA_IO_CHARACTERISTIC L"{7772E5DB-3868-4112-A1A9-F2669D106BF3}" -winrt::guid MIDI_BLE_SERVICE_CHARACTERISTIC_GUID(MIDI_BLE_SERVICE_CHARACTERISTIC); -winrt::guid MIDI_BLE_DATA_IO_CHARACTERISTIC_GUID(MIDI_BLE_DATA_IO_CHARACTERISTIC); +winrt::guid MIDI_BLE_SERVICE_UUID(MIDI_BLE_SERVICE); +winrt::guid MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID(MIDI_BLE_DATA_IO_CHARACTERISTIC); // Notes: // Write (encryption recommended, write without response is required) @@ -76,7 +76,7 @@ void OnDeviceAdded(enumeration::DeviceWatcher, enumeration::DeviceInformation in { auto serviceUuid = service.Uuid(); - if (serviceUuid == MIDI_BLE_SERVICE_CHARACTERISTIC_GUID) + if (serviceUuid == MIDI_BLE_SERVICE_UUID) { std::cout << "MIDI Service Found: " << winrt::to_string(winrt::to_hstring(serviceUuid)) << std::endl; @@ -92,7 +92,7 @@ void OnDeviceAdded(enumeration::DeviceWatcher, enumeration::DeviceInformation in // desc. //} - if (uuid == MIDI_BLE_DATA_IO_CHARACTERISTIC_GUID) + if (uuid == MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID) { std::cout << "MIDI Data IO Characteristic Found: " << winrt::to_string(winrt::to_hstring(uuid)) << std::endl; @@ -233,7 +233,7 @@ IAsyncAction TestReceivingData() { std::cout << "Using device " << winrt::to_string(bleDevice.Name()) << std::endl; - auto service = bleDevice.GetGattService(MIDI_BLE_SERVICE_CHARACTERISTIC_GUID); + auto service = bleDevice.GetGattService(MIDI_BLE_SERVICE_UUID); if (service != nullptr) @@ -258,7 +258,7 @@ IAsyncAction TestReceivingData() std::cout << "Getting MIDI Data IO characteristic" << std::endl; - auto characteristicsResult = service.GetCharacteristicsForUuidAsync(MIDI_BLE_DATA_IO_CHARACTERISTIC_GUID).get(); + auto characteristicsResult = service.GetCharacteristicsForUuidAsync(MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID).get(); if (characteristicsResult.Status() == gatt::GattCommunicationStatus::Success) { @@ -352,12 +352,181 @@ IAsyncAction TestReceivingData() } + +IAsyncAction TestReceivingData2() +{ + std::cout << "Test Receiving Data ---------------------------------------------------------" << std::endl; + + winrt::hstring id = L"BluetoothLE#BluetoothLE3c:6a:a7:f0:4e:b0-48:b6:20:1a:71:9d"; + + try + { + gatt::GattSession session{ nullptr }; + + std::cout << "Creating session" << std::endl; + + auto bleId = bt::BluetoothDeviceId::FromId(id); + + session = co_await gatt::GattSession::FromDeviceIdAsync(bleId); + + std::cout << "Session created" << std::endl; + + session.MaintainConnection(true); + + auto bleDevice = bt::BluetoothLEDevice::FromIdAsync(id).get(); + + if (bleDevice != nullptr) + { + std::cout << "Using device " << winrt::to_string(bleDevice.Name()) << std::endl; + + auto service = bleDevice.GetGattService(MIDI_BLE_SERVICE_UUID); + + if (service != nullptr) + { + std::cout << "Found service" << std::endl;; + + auto openStatus = co_await service.OpenAsync(gatt::GattSharingMode::SharedReadAndWrite); + + if (openStatus == gatt::GattOpenStatus::Success) + { + std::cout << "Service opened" << std::endl; + } + else + { + std::cout << "Unable to open service" << std::endl; + co_return; + } + + std::cout << "Getting MIDI Data IO characteristic" << std::endl; + + auto characteristicsResult = co_await service.GetCharacteristicsForUuidAsync(MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID); + + if (characteristicsResult.Status() == gatt::GattCommunicationStatus::Success) + { + if (characteristicsResult.Characteristics().Size() == 0) + { + // this is unexpected + std::cout << "Returned no characteristics for the required UUID" << std::endl; + } + else if (characteristicsResult.Characteristics().Size() > 1) + { + // TODO: Need to find out under which cases this happens + std::cout << "Returned more than one characteristic for the same UUID" << std::endl; + } + else + { + std::cout << "Returned just the one characteristic for the UUID" << std::endl; + + auto characteristic = characteristicsResult.Characteristics().GetAt(0); + + co_await characteristic.WriteClientCharacteristicConfigurationDescriptorAsync( + gatt::GattClientCharacteristicConfigurationDescriptorValue::Notify); + + + auto valueChangedHandler = [&](gatt::GattCharacteristic const& sender, gatt::GattValueChangedEventArgs const& args) + { + std::cout << "Value changed" << std::endl; + + // we get an IBuffer here + + std::cout << "Data size is " << args.CharacteristicValue().Length() << " bytes" << std::endl; + + for (int i = 0; i < args.CharacteristicValue().Length(); i++) + { + // read next byte + auto b = *(args.CharacteristicValue().data() + i); + + std::cout << "0x" << std::hex << (unsigned)b << " "; + } + + std::cout << std::endl; + }; + + // wire up ValueChanged so we actually get data + characteristic.ValueChanged(valueChangedHandler); + + while (true) + { + ::Sleep(100); + } + + session.Close(); + + } + } + + + service.Close(); + } + } + + std::cout << "Done" << std::endl; + + + // wait for incoming data indefinitely + + //auto characteristic = characteristicsResult.Characteristics().GetAt(0); + + //auto readResult = characteristic.ReadValueAsync().get(); + + //if (readResult.Status() == gatt::GattCommunicationStatus::Success) + //{ + // // we get an IBuffer here + + // for (int i = 0; i < readResult.Value().Length(); i++) + // { + // // read next byte + // auto b = *(readResult.Value().data() + i); + + // std::cout << std::hex << b << " "; + // } + + // std::cout << std::endl; + + //} + //session.Close(); + + + + + + + + + } + catch (winrt::hresult_error err) + { + // Important: using .get() means we don't actually get to handle these exceptions. + + if (err.code() == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION)) + { + std::cout << "Cannot access the BLE device because it's being used by another process" << std::endl; + } + else + { + std::cout << "HRESULT exception 0x" << std::hex << err.code() << std::endl; + } + + co_return; + } + catch (...) + { + std::cout << "Exception" << std::endl; + + co_return; + } + +} + + + int main() { init_apartment(); // TestEnumeration(); - TestReceivingData().get(); +// TestReceivingData().get(); + TestReceivingData2().get(); //system("pause > nul"); system("pause"); diff --git a/src/prototypes/ble-midi1-proto/pch.h b/src/prototypes/ble-midi1-proto/pch.h index a4f032cf..7854996e 100644 --- a/src/prototypes/ble-midi1-proto/pch.h +++ b/src/prototypes/ble-midi1-proto/pch.h @@ -9,6 +9,7 @@ #pragma once +#include #include #include From 31298acae156fbbbcc9e1a4ef3b921c5019a5f7e Mon Sep 17 00:00:00 2001 From: Pete Brown Date: Fri, 8 Mar 2024 01:35:53 -0500 Subject: [PATCH 5/5] More BLE prototyping --- src/prototypes/ble-midi1-proto/main.cpp | 178 +++++------------------- 1 file changed, 33 insertions(+), 145 deletions(-) diff --git a/src/prototypes/ble-midi1-proto/main.cpp b/src/prototypes/ble-midi1-proto/main.cpp index e222ceef..1636b377 100644 --- a/src/prototypes/ble-midi1-proto/main.cpp +++ b/src/prototypes/ble-midi1-proto/main.cpp @@ -138,28 +138,38 @@ void OnDeviceAdded(enumeration::DeviceWatcher, enumeration::DeviceInformation in void OnDeviceRemoved(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate upd) { - std::cout << "Removed: " << upd.Id().c_str() << std::endl; + std::cout << "Removed: " << winrt::to_string(upd.Id()) << std::endl; } void OnDeviceUpdated(enumeration::DeviceWatcher, enumeration::DeviceInformationUpdate upd) { - std::cout << "Updated: " << upd.Id().c_str() << std::endl; + std::cout << "-----------" << std::endl; + + std::cout << "Updated: " << winrt::to_string(upd.Id()).c_str() << std::endl; auto bleDevice = bt::BluetoothLEDevice::FromIdAsync(upd.Id()).get(); if (bleDevice != nullptr) { - std::cout << "BLE Name: " << winrt::to_string(bleDevice.Name()) << std::endl; + std::cout << "Name: " << winrt::to_string(bleDevice.Name()) << " : "; if (bleDevice.ConnectionStatus() == bt::BluetoothConnectionStatus::Connected) { - std::cout << "Current status: Connected " << std::endl; + std::cout << "Connected " << std::endl; } else { - std::cout << "Current status: Disconnected " << std::endl; + std::cout << "Disconnected " << std::endl; } } + + // spit out the updated properties + for (auto prop : upd.Properties()) + { + std::cout << winrt::to_string(prop.Key()) << std::endl; + } + + std::cout << "-----------" << std::endl; } void OnDeviceStopped(enumeration::DeviceWatcher, foundation::IInspectable) @@ -213,7 +223,7 @@ void TestEnumeration() m_DeviceStopped = m_Watcher.Stopped(winrt::auto_revoke, deviceStoppedHandler); m_DeviceEnumerationCompleted = m_Watcher.EnumerationCompleted(winrt::auto_revoke, deviceEnumerationCompletedHandler); - // need to control-c to kill this for now. By design. + // this blocks m_Watcher.Start(); } @@ -225,140 +235,6 @@ IAsyncAction TestReceivingData() winrt::hstring id = L"BluetoothLE#BluetoothLE3c:6a:a7:f0:4e:b0-48:b6:20:1a:71:9d"; - std::cout << "Attempting with id " << winrt::to_string(id) << std::endl; - - auto bleDevice = bt::BluetoothLEDevice::FromIdAsync(id).get(); - - if (bleDevice != nullptr) - { - std::cout << "Using device " << winrt::to_string(bleDevice.Name()) << std::endl; - - auto service = bleDevice.GetGattService(MIDI_BLE_SERVICE_UUID); - - - if (service != nullptr) - { - std::cout << "Found service" << std::endl;; - - // we may need to test to see if we can open it shared instead - auto openStatus = service.OpenAsync(gatt::GattSharingMode::SharedReadAndWrite).get(); - - if (openStatus == gatt::GattOpenStatus::Success) - { - std::cout << "Service opened" << std::endl; - } - else - { - std::cout << "Unable to open service" << std::endl; - return; - } - - - // get the characteristic for MIDI - - std::cout << "Getting MIDI Data IO characteristic" << std::endl; - - auto characteristicsResult = service.GetCharacteristicsForUuidAsync(MIDI_BLE_DATA_IO_CHARACTERISTIC_UUID).get(); - - if (characteristicsResult.Status() == gatt::GattCommunicationStatus::Success) - { - if (characteristicsResult.Characteristics().Size() == 0) - { - // this is unexpected - std::cout << "Returned no characteristics for the required UUID" << std::endl; - } - else if (characteristicsResult.Characteristics().Size() > 1) - { - // TODO: Need to find out under which cases this happens - std::cout << "Returned more than one characteristic for the same UUID" << std::endl; - } - else - { - // exactly one characteristic returned - std::cout << "Returned single characteristic for the required UUID" << std::endl; - - // create the session - gatt::GattSession session{ nullptr }; - - try - { - std::cout << "Creating session" << std::endl; - - session = co_await gatt::GattSession::FromDeviceIdAsync(bleDevice.BluetoothDeviceId()); - - //session = sessionResult.get(); - } - catch (winrt::hresult_error err) - { - // Important: using .get() means we don't actually get to handle these exceptions. - - if (err.code() == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION)) - { - std::cout << "Cannot access the BLE device because it's being used by another process" << std::endl; - } - else - { - std::cout << "HRESULT exception 0x" << std::hex << err.code() << std::endl; - } - - co_return; - } - catch (...) - { - std::cout << "Exception" << std::endl; - - co_return; - } - - session.MaintainConnection(true); - - // wait for incoming data indefinitely - - auto characteristic = characteristicsResult.Characteristics().GetAt(0); - - auto readResult = characteristic.ReadValueAsync().get(); - - if (readResult.Status() == gatt::GattCommunicationStatus::Success) - { - // we get an IBuffer here - - for (int i = 0; i < readResult.Value().Length(); i++) - { - // read next byte - auto b = *(readResult.Value().data() + i); - - std::cout << std::hex << b << " "; - } - - std::cout << std::endl; - - } - session.Close(); - - } - } - else - { - std::cout << "Unable to get characteristics" << std::endl; - } - - - } - else - { - std::cout << "Unable to get service" << std::endl; - } - } - -} - - -IAsyncAction TestReceivingData2() -{ - std::cout << "Test Receiving Data ---------------------------------------------------------" << std::endl; - - winrt::hstring id = L"BluetoothLE#BluetoothLE3c:6a:a7:f0:4e:b0-48:b6:20:1a:71:9d"; - try { gatt::GattSession session{ nullptr }; @@ -425,11 +301,13 @@ IAsyncAction TestReceivingData2() auto valueChangedHandler = [&](gatt::GattCharacteristic const& sender, gatt::GattValueChangedEventArgs const& args) { - std::cout << "Value changed" << std::endl; + //std::cout << "Value changed" << std::endl; // we get an IBuffer here - std::cout << "Data size is " << args.CharacteristicValue().Length() << " bytes" << std::endl; + //std::cout << "Data size is " << args.CharacteristicValue().Length() << " bytes" << std::endl; + + std::cout << "> "; for (int i = 0; i < args.CharacteristicValue().Length(); i++) { @@ -524,9 +402,19 @@ int main() { init_apartment(); -// TestEnumeration(); -// TestReceivingData().get(); - TestReceivingData2().get(); + //TestEnumeration(); + + std::thread enumerationThread(TestEnumeration); + + enumerationThread.detach(); + + + while (!m_enumerationCompleted) + { + Sleep(1000); + } + + TestReceivingData().get(); //system("pause > nul"); system("pause");