KeiganMotor を HoloLens2 から Bluetooth でつないで LED の色を操作するメモ
KeiganMotor を HoloLens2 から Bluetooth でつないで LED の色を操作するメモです。
この記事は 2022年 ゆるくすすめる ( ワンフットシーバス ) | GWアドベントカレンダー の 5/8 10日目の記事でもあります。
こちらで登壇したネタのナレッジです
https://www.1ft-seabass.jp/memo/2022/03/18/summary-xr-meeting-20220316/
2022/03 でXRミーティングに登壇してきたときの KeiganMotor を HoloLens2 から Bluetooth でつないで LED の色を操作するナレッジです。
KeiganMotor と HoloLens2 の接続

KeiganMotor に電源を入れておきます。

設定 > デバイス から、

Bluetooth とその他のデバイス > デバイスの追加 > Bluetooth とその他のデバイスを追加する を選択し、

しばらく待っていると KM-1U ではじまるデバイスが出てくるので、

クリックして接続をしてペアリングを完了しておきます。
実際に動したソースコード

HoloLens 2 側では Bluetooth を Package.appxmanifest の Capabilities で ON にしておきましょう。(よく忘れてハマる)
ソースコードはこちらです。
using UnityEngine;
using System;
#if WINDOWS_UWP
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
#endif
public class KeiganBLEOnlyLED : MonoBehaviour
{
// KeiganMotor の Service UUID
private string KEIGAN_MOTOR_SERVICE_UUID = "f140ea35-8936-4d35-a0ed-dfcd795baa8c";
// LED の Characteristic UUID
private string CHARCTERISTIC_LED_UUID = "f1400003-8936-4d35-a0ed-dfcd795baa8c";
#if WINDOWS_UWP
// HoloLens 専用の C# 処理
// BLE 系の準備
GattCharacteristicsResult characteristicsLED;
GattCharacteristicsResult characteristicsMotorControl;
DeviceWatcher deviceWatcher;
#endif
void Start()
{
Invoke("Launch", 0.5f);
}
void Update()
{
}
void Launch()
{
Debug.LogFormat("KeiganBLE Launched!");
// 接続開始
ConnectKeiganMotor();
}
void ConnectKeiganMotor()
{
// Debug.LogFormat("ConnectKeiganMotor KEIGAN_MOTOR_SERVICE_UUID:{0}", KEIGAN_MOTOR_SERVICE_UUID);
#if WINDOWS_UWP
Debug.LogFormat("ConnectKeiganMotor connecting... 1.0.3");
// WatchStart();
Task.Run(async () =>
{
// GattDeviceService で KeiganMotor の Service UUID に狙いを定める
var selectorKeigan = GattDeviceService.GetDeviceSelectorFromUuid(new Guid(KEIGAN_MOTOR_SERVICE_UUID));
// DeviceInformation で BLE でつながっているデバイスの全捜索をかける
var collectionKeigan = await DeviceInformation.FindAllAsync(selectorKeigan);
UnityEngine.WSA.Application.InvokeOnAppThread(() => {
Debug.LogFormat("deviceWatcher wait!");
}, true);
// KeiganMotor の Service UUIDと一致したものからループでつなげる
foreach (DeviceInformation infoKeigan in collectionKeigan)
{
UnityEngine.WSA.Application.InvokeOnAppThread(() => {
Debug.LogFormat("infoKeigan Name={0} IsEnabled={1} id={2}", infoKeigan.Name, infoKeigan.IsEnabled, infoKeigan.Id);
Debug.LogFormat("infoKeigan Kind={0} Pairing={1}", infoKeigan.Kind, infoKeigan.Pairing);
}, true);
// Keigan への接続
GattDeviceService serviceKeigan = await GattDeviceService.FromIdAsync(infoKeigan.Id);
UnityEngine.WSA.Application.InvokeOnAppThread(() => {
// serviceKeigan に関するメッセージを出力
Debug.LogFormat("serviceKeigan {0}", serviceKeigan);
}, true);
// LED Characteristic 取得
characteristicsLED = await serviceKeigan.GetCharacteristicsForUuidAsync(new Guid(CHARCTERISTIC_LED_UUID));
// LED Characteristic 取得成功時
if (characteristicsLED.Status == GattCommunicationStatus.Success)
{
// OK
UnityEngine.WSA.Application.InvokeOnAppThread(() => {
Debug.LogFormat("characteristicsLED Success!");
// Debug.LogFormat("characteristicsLED.Characteristics.Count {0}", characteristicsLED.Characteristics.Count);
}, true);
// 初回は青く光る
WriteDataKeiganMotorLED(1, 0, 0, 255);
}
}
});
#endif
}
// LED を実際に光らせる処理
// RGB で指定できる
public void WriteDataKeiganMotorLED(int mode, int r, int g, int b)
{
#if WINDOWS_UWP
Task.Run(async () =>
{
UnityEngine.WSA.Application.InvokeOnAppThread(() => {
Debug.LogFormat("WriteDataKeiganMotorLED mode {0} red {1} green {2} blue {3}", mode, r, g, b);
}, true);
byte[] comPress = { Convert.ToByte(mode), Convert.ToByte(r), Convert.ToByte(g), Convert.ToByte(b) };
GattCommunicationStatus status = await characteristicsLED.Characteristics[0].WriteValueAsync(comPress.AsBuffer());
UnityEngine.WSA.Application.InvokeOnAppThread(() => {
Debug.LogFormat("GattCommunicationStatus {0}", status.ToString());
}, true);
});
#endif
}
}
このコードで KeiganBLEOnlyLED という GameObject に以下の KeiganBLEOnlyLED.cs を割り当てていると、接続成功後に LED が青く光ります。

とくにデータを送る部分は UWP(C#)でSwitchBot(BLEデバイス)を操作する 記事の WriteValueAsync あたりを参考にさせていただきました。