ã¯ããã«
ã©ãããåéã§ãã
ä»åã¯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ã§ãåããªãã£ãé¨åãä¸é¨ä¿®æ£ãå ãã¦åãããã«ãã¾ããã
- ã¯ããã«
- ããªãã§ã©ã«
- ã»ã³ãã©ã«
- Unityã§ã¡ãã»ã¼ã¸ãåãåã
- ã¾ã¨ã
- åè
ããªãã§ã©ã«
Bluetoothæè¼ãã¤ã³ã³
BLEéä¿¡ã«ã¯Blutooth対å¿SoCãnRF52840ããæè¼ãããã¤ã³ã³ã使ç¨ãã¾ããã
ãã®ãã¤ã³ã³ã¯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 UUID #define USER_SERVICE_UUID " çæããUUID " #define WRITE_CHARACTERISTIC_UUID " çæããUUID " #define NOTIFY_CHARACTERISTIC_UUID " çæããUUID " #define DEVICE_NAME " BLEããã¤ã¹ " // BLE Service and Characteristics BLEService userService = BLEService(USER_SERVICE_UUID); BLECharacteristic notifyChar = BLECharacteristic(NOTIFY_CHARACTERISTIC_UUID); // Connection handle for notifications uint16_t connectionHandle = BLE_CONN_HANDLE_INVALID; void connectionCallback(uint16_t conn_handle) { connectionHandle = conn_handle; // Save connection handle Serial.println("Connected"); } void disconnectionCallback(uint16_t conn_handle, uint8_t reason) { connectionHandle = BLE_CONN_HANDLE_INVALID; // Invalidate connection handle Serial.println("Disconnected"); Bluefruit.Advertising.start(); // Restart advertising } 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"); // Initialize serial and BLE Bluefruit.begin(); Bluefruit.setTxPower(4); // Set BLE device name Bluefruit.setName(DEVICE_NAME); // Initialize the service and characteristics userService.begin(); notifyChar.setProperties(CHR_PROPS_NOTIFY); notifyChar.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS); notifyChar.begin(); // Set connection and disconnection callbacks Bluefruit.Periph.setConnectCallback(connectionCallback); Bluefruit.Periph.setDisconnectCallback(disconnectionCallback); // Start advertising 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(); // Check if 1 second has passed if (currentTime - lastSendTime >= 1000) { lastSendTime = currentTime; sendHello(); // Send "Hello" notification } }
解説
BLEã®æºå
UUIDã®çæã¯ä»¥ä¸ã®ãããªãµã¤ããããããã®ç°å¢ã§çæãããã¨ã§ã²ãããã¦ãã ããã
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 # BLE UUIDs USER_SERVICE_UUID = " çæããUUID " NOTIFY_CHARACTERISTIC_UUID = " çæããUUID " DEVICE_NAME = " BLEããã¤ã¹ " # TCP settings TCP_HOST = "127.0.0.1" # Unityå´ã§å¾ æ©ããIPã¢ãã¬ã¹ (ãã¼ã«ã«ãã¹ã) TCP_PORT = 12345 # Unityã§è¨å®ãããã¼ãçªå· # Global TCP connection tcp_client = None # Callback for BLE notifications def notification_handler(sender, data): message = data.decode("utf-8") print(f"Received from BLE: {message}") # Send data to Unity via TCP 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(): # Set up TCP connection to Unity 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 # Scan for BLE devices 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: # Wait for services to load services = await client.get_services() # Debug: Print all services and characteristics print("Available services and characteristics:") for service in services: print(f"- Service: {service.uuid}") for char in service.characteristics: print(f" - Characteristic: {char.uuid}") # Check if the target service and characteristic are available 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...") # Set notification callback 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); // ãã¼ã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ã§å®çµããæ¸ãæ¹ããã£ãããææããã ãããã§ãã