ã¯ããã«
ã©ãããåéã§ãã
ä»åã¯Bluetoothããã¤ã¹ã¨Unityéã§éä¿¡ãè¡ã£ã¦ã¿ã¾ããã
ãã ãUnityã§ã®Bluetoothã«é¢ãã¦ã¯OSåºæã®åé¡ãå¤ãããã®ã¾ã¾ã®Unityä¸ã ãã§BLEãå®çµããããã¨ã¯ã§ãã¾ããã§ãããããã«é¢ãã¦ã¯æ§ã
è¨äºãæ¢ãã¾ãããä»ã¾ã§å®ç§ã«ã¹ãã¼ããªè§£æ±ºãã¦ãããã®ããªãã®ã§ãç¾ç¶ã¯ãªãã®ã ã¨æãã¾ãã
ã¢ãã¤ã«åãã«ã¯ä»¥ä¸ã®ãããªã¢ã»ãããããã¾ããã
Bluetooth LE for iOS, tvOS and Android | ネットワーク | Unity Asset Store
ã§ãä»åã¯ã©ã®ããã«è§£æ±ºãããã¨è¨ãã¾ãã¨ã
åç´ã«Pythonã§BLEç¨ã®ã³ã¼ããåãããUnityã¨ãã¼ã«ã«ã§TCPãã¦éãå½¢ã«ãã¾ããã
ã¾ãã§è§£æ±ºãã¦ãªããããªããã§ã¯ããã¾ãããã¾ããã£ããããã§ãããã¨ãããã¨ã«ãªãã¾ããã
ãªã®ã§ãUnityã§æ¸ããã®ã¯TCPé¨åã ããªã®ã§ããã®è¨äºã¯ã»ã¨ãã©nRF52ã¨Windowséã®BLEã«é¢ãã解説ãã»ã¨ãã©ã§ãã
ã³ã¼ãã¯ååãããChatGPTã§ãåããªãã£ãé¨åãä¸é¨ä¿®æ£ãå ãã¦åãããã«ãã¾ããã
BLEéä¿¡ã«ã¯Blutooth対å¿SoCãnRF52840ããæè¼ãããã¤ã³ã³ã使ç¨ãã¾ããã
akizukidenshi.com
ãã®ãã¤ã³ã³ã¯Adafruit nRF52 Bootloaderãçµã¿è¾¼ã¾ãã¦ããã®ã§ArduinoIDEã«ã¦éçºãå¯è½ã§ãã
ãã¼ãããã¼ã¸ã£ã¼ã®è¿½å
ArduinoIDEã®Adafruit nRF52ã¢ããªã³è¿½å ç¨URLãæè¿å¤æ´ããããããªã®ã§ããã«ã¡ã¢ã£ã¨ãã¾ãã
https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
ãããPreferencesã®Additional Boards Manager URLsã«è¿½å ãã¦ãã ããã
ããã«ãã£ã¦nRF52ã®ãã¼ãã«æ¸ãè¾¼ããããã«ãªãã¾ãã
ã³ã¼ã
ãã¹ãç¨ã«1ç§ããã«ãHelloãã¨ããã¡ãã»ã¼ã¸ãé£ã°ãã ãã®ã³ã¼ãã§ãã
#include <Arduino.h>
#include <bluefruit.h>
#define USER_SERVICE_UUID " çæããUUID "
#define WRITE_CHARACTERISTIC_UUID " çæããUUID "
#define NOTIFY_CHARACTERISTIC_UUID " çæããUUID "
#define DEVICE_NAME " BLEããã¤ã¹ "
BLEService userService = BLEService(USER_SERVICE_UUID);
BLECharacteristic notifyChar = BLECharacteristic(NOTIFY_CHARACTERISTIC_UUID);
uint16_t connectionHandle = BLE_CONN_HANDLE_INVALID;
void connectionCallback(uint16_t conn_handle) {
connectionHandle = conn_handle;
Serial.println("Connected");
}
void disconnectionCallback(uint16_t conn_handle, uint8_t reason) {
connectionHandle = BLE_CONN_HANDLE_INVALID;
Serial.println("Disconnected");
Bluefruit.Advertising.start();
}
void sendHello() {
if (connectionHandle != BLE_CONN_HANDLE_INVALID) {
const char* message = "Hello";
notifyChar.notify(connectionHandle, (uint8_t*)message, strlen(message));
Serial.println("Sent: Hello");
}
}
void setup() {
Serial.begin(115200);
while ( !Serial ) delay(10);
Serial.println("Start BLE Test");
Bluefruit.begin();
Bluefruit.setTxPower(4);
Bluefruit.setName(DEVICE_NAME);
userService.begin();
notifyChar.setProperties(CHR_PROPS_NOTIFY);
notifyChar.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
notifyChar.begin();
Bluefruit.Periph.setConnectCallback(connectionCallback);
Bluefruit.Periph.setDisconnectCallback(disconnectionCallback);
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addName();
Bluefruit.Advertising.addService(userService);
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244);
Bluefruit.Advertising.start();
Bluefruit.Advertising.setFastTimeout(30);
Serial.println("BLE device is ready and advertising!");
}
void loop() {
static unsigned long lastSendTime = 0;
unsigned long currentTime = millis();
if (currentTime - lastSendTime >= 1000) {
lastSendTime = currentTime;
sendHello();
}
}
解説
BLEã®æºå
UUIDã®çæã¯ä»¥ä¸ã®ãããªãµã¤ããããããã®ç°å¢ã§çæãããã¨ã§ã²ãããã¦ãã ããã
uuid.doratool.com
BLEService userService = BLEService(USER_SERVICE_UUID);
BLECharacteristic writeChar = BLECharacteristic(WRITE_CHARACTERISTIC_UUID);
BLECharacteristic notifyChar = BLECharacteristic(NOTIFY_CHARACTERISTIC_UUID);
ãã®é¨åã§Serviceã¨Characteristicã«UUIDãç»é²ãã¦ãã¾ãã
Characteristicã®è¨å®
userService.begin();
setProperties
setProperties(CHR_PROPS_WRITE)
Characteristicã®ããããã£ãWriteã«è¨å®ãã¾ã
setProperties(CHR_PROPS_NOTIFY)
Characteristicã®ããããã£ãNotifyã«è¨å®ãã¾ã
setPermission
setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS)
1ã¤ç®ã®å¼æ°ãReadã®æ¨©éã§ã2ã¤ç®ã®å¼æ°ãWriteã®æ¨©éã§ãã
callbackã®è¨å®
Bluefruit.Periph.setConnectCallback
â æ¥ç¶æã®ã³ã¼ã«ããã¯ã®è¨å®
Bluefruit.Periph.setDisconnectCallback
â åææã®ã³ã¼ã«ããã¯ã®è¨å®
advertising
addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
常æãã¢ããã¿ã¤ã¸ã³ã°ãã±ãããçºä¿¡ããã¢ã¼ãã§ãã
ã»ã³ãã©ã«
BLEããã¤ã¹è¦ã¤ãã¦ãã¡ãã»ã¼ã¸ãåãåãWindowså´ã®ã³ã¼ãã§ãã
Bleakã®ã¤ã³ã¹ãã¼ã«
Pythonã®BLEéä¿¡ã«ã¯Bleakã¨ããã©ã¤ãã©ãªã使ç¨ããã®ãè¯ãããã§ãã
pipã³ãã³ãã§ã¤ã³ã¹ãã¼ã«ãè¡ã£ã¦ãã ããã
pip install bleak
ã³ã¼ã
Blutoothããã¤ã¹ã®ã¹ãã£ã³ãè¡ã£ã¦ãç®çã®ããã¤ã¹ãè¦ã¤ãããæ¥ç¶ãéå§ãã¾ãã
import asyncio
import socket
from bleak import BleakClient, BleakScanner
USER_SERVICE_UUID = " çæããUUID "
NOTIFY_CHARACTERISTIC_UUID = " çæããUUID "
DEVICE_NAME = " BLEããã¤ã¹ "
TCP_HOST = "127.0.0.1"
TCP_PORT = 12345
tcp_client = None
def notification_handler(sender, data):
message = data.decode("utf-8")
print(f"Received from BLE: {message}")
if tcp_client:
try:
tcp_client.sendall(message.encode("utf-8"))
print(f"Sent to Unity: {message}")
except Exception as e:
print(f"Error sending data to Unity: {e}")
async def main():
global tcp_client
try:
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_client.connect((TCP_HOST, TCP_PORT))
print(f"Connected to Unity at {TCP_HOST}:{TCP_PORT}")
except Exception as e:
print(f"Failed to connect to Unity: {e}")
return
print("Scanning for BLE devices...")
devices = await BleakScanner.discover()
target_device = None
for device in devices:
print(f"Found device: {device.name} - {device.address}")
if device.name == DEVICE_NAME:
target_device = device
break
if not target_device:
print(f"Device {DEVICE_NAME} not found. Exiting.")
return
print(f"Connecting to {DEVICE_NAME} ({target_device.address})...")
async with BleakClient(target_device.address) as client:
services = await client.get_services()
print("Available services and characteristics:")
for service in services:
print(f"- Service: {service.uuid}")
for char in service.characteristics:
print(f" - Characteristic: {char.uuid}")
if USER_SERVICE_UUID not in [s.uuid for s in services]:
print(f"Service {USER_SERVICE_UUID} not found on the device.")
return
if NOTIFY_CHARACTERISTIC_UUID not in [c.uuid for s in services for c in s.characteristics]:
print(f"Characteristic {NOTIFY_CHARACTERISTIC_UUID} not found on the device.")
return
print(f"Connected to {DEVICE_NAME}. Subscribing to notifications...")
await client.start_notify(NOTIFY_CHARACTERISTIC_UUID, notification_handler)
print("Listening for notifications. Press Ctrl+C to exit.")
try:
while True:
await asyncio.sleep(1)
except KeyboardInterrupt:
print("Stopping notifications...")
await client.stop_notify(NOTIFY_CHARACTERISTIC_UUID)
print("Disconnected.")
finally:
tcp_client.close()
if __name__ == "__main__":
asyncio.run(main())
解説
ã¹ãã£ã³
BleakScanner.discover()
ã§BLEããã¤ã¹ã®ã¹ãã£ã³ãè¡ã£ã¦ãæ¥ç¶ããããã¤ã¹ãæ¢ãã¾ãã
æ¥ç¶ããServiceã¨Characteristicãæ¢ã
client.get_services()
ã§ãµã¼ãã¹ãåå¾ãã¾ãã
service.characteristics
ã§ãã®Serviceã®Characteristicãåå¾ã§ãã¾ãã
Notifyãå¾
ã¤
client.start_notify(UUID, å®è¡ããã¡ã½ãã)
ã§ããã¤ã¹ããNotifyãéããã¦ããã®ãå¾
ã¡ã¾ãã
Unityã§ã¡ãã»ã¼ã¸ãåãåã
ãã¡ãã¯åç´ã«TCPãµã¼ãã¼ãç«ã¦ã¦ããã ãã§ã
ã³ã¼ã
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;
public class TCPTest : MonoBehaviour
{
private TcpListener server;
private bool isRunning = true;
void Start()
{
StartServer();
}
void OnDisable()
{
isRunning = false;
server?.Stop();
}
void StartServer()
{
try
{
server = new TcpListener(IPAddress.Any, 12345);
server.Start();
Debug.Log("Server started");
server.BeginAcceptTcpClient(OnClientConnected, null);
}
catch (Exception ex)
{
Debug.LogError($"Error starting server: {ex.Message}");
}
}
void OnClientConnected(IAsyncResult result)
{
if (!isRunning) return;
TcpClient client = server.EndAcceptTcpClient(result);
Debug.Log("Client connected");
server.BeginAcceptTcpClient(OnClientConnected, null);
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
stream.BeginRead(buffer, 0, buffer.Length, OnDataReceived, new Tuple<NetworkStream, byte[]>(stream, buffer));
}
void OnDataReceived(IAsyncResult result)
{
var state = (Tuple<NetworkStream, byte[]>)result.AsyncState;
NetworkStream stream = state.Item1;
byte[] buffer = state.Item2;
int bytesRead = stream.EndRead(result);
if (bytesRead > 0)
{
string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Debug.Log($"Received: {message}");
stream.BeginRead(buffer, 0, buffer.Length, OnDataReceived, state);
}
else
{
stream.Close();
}
}
}
Unityã«ã¡ãã»ã¼ã¸ãæ¥ãï¼
ã¾ã¨ã
ãã¼ããæ¬å½ã¯Hololensä¸ã¨ãQuestä¸ã®ã¢ããªã±ã¼ã·ã§ã³ã§ããããããUnityã§å
¨é¨æ¸ããããã ããªãâ¦
NuGet For Unityã§PureC#ã®Windows.Devices使ããããããï¼ï¼ã£ã¦æã£ããã§ãããã ãã ã£ãã®ã§ãä»åã®ææ³ã«è½ã¡çãã¾ããã
ã©ãªããUnityã§å®çµããæ¸ãæ¹ããã£ãããææããã ãããã§ãã
åè
www.mkbtm.jp
www.mkbtm.jp
qiita.com
qiita.com
www.musen-connect.co.jp