HarmonyOS 5.0+におけるセンサー、Bluetooth、位置情報のハードウェア連携実装

HarmonyOS 5.0以降では、デバイスのハードウェア機能を安全かつ効率的に活用するための統一APIが提供されている。本稿では、センサーデータ取得、Bluetooth通信、位置情報サービスの実装方法を解説する。

1. センサーデータの取得

HarmonyOSは複数のセンサータイプに対応しており、@ohos.sensorモジュールを通じてアクセス可能である。

import sensor from '@ohos.sensor';
import { BusinessError } from '@ohos.base';

class MotionDetector {
  private manager: sensor.SensorManager | null = null;
  private listener: sensor.SensorEventListener | null = null;

  async init(): Promise<void> {
    try {
      this.manager = await sensor.getSensorManager();
    } catch (err) {
      console.error('センサー初期化失敗:', (err as BusinessError).message);
    }
  }

  async startMonitoring(): Promise<void> {
    if (!this.manager) return;

    const accel = await this.manager.getDefaultSensor(sensor.SensorType.SENSOR_TYPE_ACCELEROMETER);
    this.listener = await this.manager.registerListener(
      accel,
      (event) => {
        const [x, y, z] = event.data;
        const magnitude = Math.sqrt(x ** 2 + y ** 2 + z ** 2);
        if (magnitude > 14.0) {
          console.log('急激な動きを検出');
        }
      },
      { interval: 200 }
    );
  }

  async stop(): Promise<void> {
    if (this.manager && this.listener) {
      await this.manager.unregisterListener(this.listener);
    }
  }
}

2. Bluetooth通信の実装

Bluetooth ClassicおよびBLE(Bluetooth Low Energy)の両方に対応している。

2.1 Bluetooth Classicによるデータ送受信

import bluetooth from '@ohos.bluetooth';

class RfcommClient {
  private socketMap = new Map<string, bluetooth.BluetoothSocket>();

  async connect(device: bluetooth.BluetoothDevice): Promise<boolean> {
    try {
      const sock = await device.createRfcommSocketToServiceRecord(
        '00001101-0000-1000-8000-00805F9B34FB'
      );
      await sock.connect();
      this.socketMap.set(device.deviceId, sock);

      sock.on('message', (buf: Uint8Array) => {
        const text = new TextDecoder().decode(buf);
        console.log(`受信: ${text}`);
      });

      return true;
    } catch (e) {
      console.error('接続失敗:', (e as BusinessError).message);
      return false;
    }
  }

  async send(id: string, msg: string): Promise<boolean> {
    const sock = this.socketMap.get(id);
    if (!sock) return false;
    try {
      await sock.send(new TextEncoder().encode(msg));
      return true;
    } catch (e) {
      console.error('送信エラー:', (e as BusinessError).message);
      return false;
    }
  }
}

2.2 BLEデバイスとの連携

import ble from '@ohos.bluetooth.ble';

class BleHeartRateMonitor {
  async scanAndConnect(): Promise<void> {
    const scanner = await ble.createScanner();
    scanner.on('deviceDiscover', async (dev) => {
      if (dev.name?.includes('HRM')) {
        await scanner.stopScan();
        const client = await ble.createGattClientDevice(dev.deviceId);
        await client.connect();
        const services = await client.discoverServices();
        for (const svc of services) {
          for (const chr of svc.characteristics) {
            if (chr.properties.notify) {
              await client.subscribeToCharacteristic(chr);
            }
          }
        }
      }
    });
    await scanner.startScan();
    setTimeout(() => scanner.stopScan(), 8000);
  }
}

3. 位置情報サービスの活用

@ohos.geolocationモジュールにより、高精度または省電力モードでの位置取得が可能。

3.1 一時的な位置取得

import geolocation from '@ohos.geolocation';

async function fetchLocation(): Promise<geolocation.Location | null> {
  const mgr = geolocation.getLocationManager();
  const req: geolocation.CurrentLocationRequest = {
    priority: geolocation.LocationRequestPriority.FAST_FIRST_FIX,
    maxAccuracy: 30,
    timeout: 8000
  };
  try {
    const loc = await mgr.getCurrentLocation(req);
    console.log(`緯度: ${loc.latitude}, 経度: ${loc.longitude}`);
    return loc;
  } catch (e) {
    console.error('位置取得失敗:', (e as BusinessError).message);
    return null;
  }
}

3.2 持続的な位置監視とジオフェンス

class GeoTracker {
  private mgr: geolocation.LocationManager;
  private fenceCenter = { lat: 35.6895, lon: 139.6917 };
  private radiusMeters = 500;

  constructor() {
    this.mgr = geolocation.getLocationManager();
  }

  async startTracking(): Promise<void> {
    const req: geolocation.LocationRequest = {
      priority: geolocation.LocationRequestPriority.BALANCED_POWER_ACCURACY,
      timeInterval: 6000,
      distanceInterval: 15
    };

    await this.mgr.requestLocationUpdates(req, (loc) => {
      const dist = this.haversineDistance(
        loc.latitude, loc.longitude,
        this.fenceCenter.lat, this.fenceCenter.lon
      );
      if (dist <= this.radiusMeters) {
        console.log('ジオフェンス内に進入');
      }
    });
  }

  private haversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
    const R = 6371000;
    const φ1 = lat1 * Math.PI / 180;
    const φ2 = lat2 * Math.PI / 180;
    const Δφ = (lat2 - lat1) * Math.PI / 180;
    const Δλ = (lon2 - lon1) * Math.PI / 180;

    const a = Math.sin(Δφ / 2) ** 2 +
              Math.cos(φ1) * Math.cos(φ2) *
              Math.sin(Δλ / 2) ** 2;
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    return R * c;
  }
}

4. エラーハンドリングとパフォーマンス最適化

ハードウェア操作では、権限不足やリソース競合などのエラーが発生しやすいため、包括的なエラーハンドリングが必要。

class SafeHardwareInvoker {
  static async execute<T>(name: string, fn: () => Promise<T>): Promise<T | null> {
    try {
      return await fn();
    } catch (err) {
      const e = err as BusinessError;
      switch (e.code) {
        case 201:
          console.warn('必要な権限が付与されていません');
          break;
        case 202:
          console.warn('このデバイスは対応していません');
          break;
        default:
          console.error(`${name}で未定義エラー: ${e.message}`);
      }
      return null;
    }
  }
}

// 使用例
const loc = await SafeHardwareInvoker.execute('位置取得', fetchLocation);

5. プライバシー保護対策

位置情報やセンサーデータは個人を特定可能な情報となるため、匿名化や最小限の収集が推奨される。

function anonymize(loc: geolocation.Location) {
  return {
    lat: Math.floor(loc.latitude * 100) / 100,   // 小数点第2位以下切り捨て
    lon: Math.floor(loc.longitude * 100) / 100,
    accuracy: Math.ceil(loc.accuracy / 50) * 50  // 精度を50m単位に丸め
  };
}

タグ: HarmonyOS Bluetooth BLE センサー 位置情報

9月20日 03:15 投稿