본문 바로가기

개발 일지

[C#] .NET 프레임워크로 윈도우에서 Bluetooth 사용하기

데스크탑에서 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 이 설명되어 있다.

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 가 출력되는 것을 확인할 수 있다.

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