데스크탑에서 BLE 디바이스와 소통하는 앱을 제작해야 하는 태스크를 맡게 되어 Windows 플랫폼에서 구동하는 데스크탑 앱을 제작하기로 결정하였다. Cross-platform 으로 동작하는 bleak 이라는 패키지도 있지만, python 으로 작성한 앱은 pyinstaller 와 같은 별도의 패키징 단계를 거치지 않는 이상 배포에 어려움이 있어서, Windows Native 한 환경에서 .NET 프레임워크 위에서 C# 으로 작성하여 개발을 진행하기로 결정했다.
GitHub - hbldh/bleak: A cross platform Bluetooth Low Energy Client for Python using asyncio
A cross platform Bluetooth Low Energy Client for Python using asyncio - GitHub - hbldh/bleak: A cross platform Bluetooth Low Energy Client for Python using asyncio
github.com
"C# 으로 Bluetooth 통신하기"를 검색하면 Microsoft 의 공식 Documentation 을 찾을 수 있다.
Bluetooth GATT 클라이언트 - UWP applications
이 문서에서는 UWP(유니버설 Windows 플랫폼) 앱에 대해 GATT(Bluetooth Generic Attribute) 클라이언트 API를 사용하는 방법을 보여 줍니다.
learn.microsoft.com
문서를 잘 읽어보면, 주변 디바이스를 쿼리하는 방법부터 시작해서 Characteristic 을 읽어오는 방법까지 잘 작성이 되어 있다. 위 문서에 기반해서 프로젝트를 진행하였다.
Visual Studio 프로젝트 Setup
Visual Studio 를 설치한다. 개인 프로젝트로 진행한다면 Community 버전으로 다운받으면 된다.
Visual Studio 를 열고, 새 프로젝트를 생성한다.
Visual Studio 에서 제공하는 다양한 템플릿을 볼 수 있다. GUI 와 함께 만들어야 하는 PoC 의 경우에는 Form Application 이 간편하지만, UI 를 구성하는 것으로도 많은 부분을 차지하기 때문에, 데모의 편의성을 위해서 콘솔 앱 템플릿을 선택한다.
템플릿을 선택하면 프로젝트 이름을 설정할 수 있다. 편의상 BluetoothTest 로 작성하였다.
프로젝트가 생성되면 드디어 코드를 작성할 수 있다. 기본적으로 Program.cs 파일이 생성된 것을 확인할 수 있다. 미리 작성되어 있는 코드를 전부 삭제하고, Bluetooth 를 사용하기 위한 코드를 작성해 보자. 위의 문서에서는 Windows.Devices.Bluetooth API 를 사용한다. 해당 네임스페이스를 가져와 보자.
using 문을 작성하자, 네임스페이스를 찾을 수 없다는 에러가 난다. 참조한 문서에는 UWP 애플리케이션 프로젝트를 상정하기 때문에 Windows API 를 바로 사용할 수 있지만, 우리는 일반 Console App 템플릿이기 때문에, 별도의 설정을 해 주어야 한다. 해결 방법은 다음 공식문서에 기술되어 있다.
데스크톱 앱에서 Windows 런타임 API 호출 - Windows apps
Windows 런타임 API를 사용하여 Windows 사용자용 데스크톱 애플리케이션을 개선하세요.
learn.microsoft.com
.NET 6 이전 버전에서는 Microsoft.Windows.SDK.Contracts NuGet 패키지를 설치해야 하지만, .NET 6 이상에서는 TFM(대상 프레임워크 모니커)를 지정하여 해결할 수 있다. 다시 말해, 이 앱은 Windows 에서만 구동하는 앱이라고 프로젝트 설정에 명시할 수 있다. 필자는 .NET 8 버전을 사용했기 때문에, 후자의 방법을 이용할 수 있었다.
프로젝트 -> 프로젝트 파일 편집
을 선택해서 프로젝트 파일을 연다.
프로젝트 생성 후 아무런 설정도 건드리지 않았다면, 다음과 같은 프로젝트 파일을 확인할 수 있다.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
공식문서 에 나온 대로, TargetFramework 값을 변경해 준다.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
프로젝트 파일을 저장하고, 다시 Program.cs 로 돌아와 보면, 에러가 사라진 것을 확인할 수 있다.
주변 디바이스 쿼리
DeviceWatcher 를 이용하여 주변 Bluetooth 디바이스를 쿼리해 보자.
단순하게 페어링 되지 않은 디바이스를 전부 쿼리해 보기 위해, Selector 인자만 넣어서 watcher 를 생성한다.
using Windows.Devices.Bluetooth;
using Windows.Devices.Enumeration;
var watcher = DeviceInformation.CreateWatcher(
BluetoothDevice.GetDeviceSelectorFromPairingState(false)
);
공식 문서에 DeviceWatcher 의 lifecycle 이 설명되어 있다.
디바이스가 추가되었을 때에, DeviceWatcher.Added 이벤트가 발생한다. 새로운 디바이스를 검색했을 때에, 디바이스의 이름을 출력하고, watcher 가 멈췄을 때에도 추가적으로 메세지를 출력하도록 하자.
void Watcher_Added(DeviceWatcher sender, DeviceInformation info)
{
Console.WriteLine($"Found {info.Name}: {info.Id}");
}
void Watcher_Stopped(DeviceWatcher sender, object args)
{
Console.WriteLine("Watcher Stopped");
}
watcher.Added += Watcher_Added;
watcher.Stopped += Watcher_Stopped;
DeviceWatcher 의 필수적인 이벤트를 implement 하기 전에 DeviceWatcher.Start
를 호출하면 에러가 나기 때문에, 이벤트를 먼저 작성해야 한다. 필요한 이벤트를 모두 구현했으면, DeviceWatcher 의 스캔을 시작해보자.
watcher.Start();
Console.WriteLine("Press ENTER to quit");
Console.ReadLine();
watcher.Stop();
프로그램을 실행하면, 잠시 후 검색된 디바이스와 이름과 Id 가 출력되는 것을 확인할 수 있다.
Device 의 Service와 Characteristic 으로부터 데이터를 읽어 오는 방법은 다음 글에서 다뤄보도록 하자.
전체 코드
using Windows.Devices.Bluetooth;
using Windows.Devices.Enumeration;
var watcher = DeviceInformation.CreateWatcher(
BluetoothDevice.GetDeviceSelectorFromPairingState(false)
);
void Watcher_Added(DeviceWatcher sender, DeviceInformation info)
{
Console.WriteLine($"Found {info.Name}: {info.Id}");
}
void Watcher_Stopped(DeviceWatcher sender, object args)
{
Console.WriteLine("Watcher Stopped");
}
watcher.Added += Watcher_Added;
watcher.Stopped += Watcher_Stopped;
watcher.Start();
Console.WriteLine("Press ENTER to quit");
Console.ReadLine();
watcher.Stop();
다음글
[C#] .NET 프레임워크로 윈도우에서 Bluetooth 사용하기 (2)
이전 포스트에서 C# Console Application 에서 BLE 를 사용할 수 있도록 설정하고, 주변의 디바이스를 쿼리하는 부분까지 다뤄보았다. 이번 포스트에서는 실제로 Device 의 characteristic 으로부터 데이터를
dev-seb.tistory.com
'개발 일지' 카테고리의 다른 글
[k8s] minikube 로 k8s 클러스터 실습하기 (0) | 2024.01.12 |
---|---|
[Windows] NSIS 를 이용해서 Forms Application 배포하기 (0) | 2024.01.08 |
[Windows] NSIS 를 이용해서 설치 파일 패키징하기 (0) | 2024.01.08 |
[C#] .NET 프레임워크로 윈도우에서 Bluetooth 사용하기 (2) (0) | 2024.01.04 |
[v0 by Vercel] 홈페이지를 디자인해주고 코딩까지 해주는 AI 를 사용해보았다 (0) | 2023.12.14 |