2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[SwiftUI + Core Bluetooth]ソニーの IoT 機器「MESH」と iOS アプリを接続する

Posted at

記事の概要

  • ソニーが開発、販売している IoT 機器 「MESH」 と iOS アプリを接続する
    スクリーンショット 2022-12-04 15.25.04.png

今回の開発環境

PC

項目 内容 備考
PC MacBook Air M1 (2020) メモリ:16GB ストレージ:1TB
macOS Ventura(ver 13.0)
iPhone iPhone SE(2nd) iOS 16.1
IDE Xcode ver 14.1

MESH

今回は 「ボタン」 を使用した

アプリの内容

  • MESH の 「ボタン」 を押すと、iOS アプリの画面に 「tapped.」 「double tapped.」 と表示する。

コード (一部抜粋)

"BLEで複数デバイスとコネクトする - Qiita" 中で紹介されているコードを流用し、MESH が発した情報を取得する

※ 全体のコードは GitHub を参照してください

コード (MESH との接続に関するもの)

BLEApp.swift
import SwiftUI
import CoreBluetooth

@main
struct BLEApp: App {
    @StateObject var viewModel: DeviceManager = DeviceManager()
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(viewModel)
        }
    }
}
Device.swift
import Foundation
import CoreBluetooth

final class Device: NSObject {
    let peripheral: CBPeripheral
    let rssi: NSNumber
    
    init(peripheral: CBPeripheral, rssi: NSNumber){
        self.peripheral = peripheral
        self.rssi = rssi
        super.init()
        peripheral.delegate = self
    }
}

extension Device: CBPeripheralDelegate {}

DeviceManager.swift
import Foundation
import CoreBluetooth

class DeviceManager: NSObject, ObservableObject {
    private let centralManager: CBCentralManager
    private let services:[CBUUID] = [CBUUID(string: MESH.UUID.description)]
    @Published var devices:[Device] = []
    @Published var recievedData:[Int] = []
    
    override init(){
        centralManager = CBCentralManager(delegate: nil, queue: nil)
        super.init()
        centralManager.delegate = self
        self.scan()
    }
    
    func scan(){
        centralManager.scanForPeripherals(withServices: services, options: nil)
    }
    
    func stopScan(){
        centralManager.stopScan()
    }
    
    func connect(perioheral: CBPeripheral){
        centralManager.connect(perioheral, options: nil)
    }
    
    func disconnect(peripheral: CBPeripheral){
        centralManager.cancelPeripheralConnection(peripheral)
    }
    
    func analyze(data: Data) -> [Int] {
        var result:[Int] = []
        data.map { i in result.append(Int(i)) }
        return result
    }
}

extension DeviceManager: CBCentralManagerDelegate {
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            centralManager.scanForPeripherals(withServices: services)
        }
    }
    
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        let device = Device(peripheral: peripheral, rssi: RSSI)
        if let index = devices.firstIndex(where: { $0.peripheral.identifier == device.peripheral.identifier }) {
            devices[index] = device
        } else {
            devices.append(device)
        }
        devices.map { device in self.centralManager.connect(device.peripheral, options: nil)}
    }
    
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        guard let peripheral = devices.first?.peripheral else { return }
        peripheral.delegate = self
        peripheral.discoverServices(services)
        print("centralManager did connect peripheral: \(peripheral.name?.description ?? "unnnamed")")
    }
    
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        print("centralManager did disconnect peripheral")
    }
    
    
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        print("did update Value for executed")
        print(characteristic.description)
        guard let data = characteristic.value else { return }
        recievedData = analyze(data: data)
    }
    
}

extension DeviceManager: CBPeripheralDelegate {
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        for service in peripheral.services! {
            peripheral.discoverCharacteristics(nil, for: service)
        }
        print("did  DiscoverServices executed")
    }
    
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        let byteArray: [UInt8] = [ 0x00, 0x02, 0x01, 0x03]
        let data = Data(byteArray)
        guard let characteristics = service.characteristics else { return }
        
        for characteristic in characteristics {
            peripheral.setNotifyValue(true, for: characteristic)
            peripheral.writeValue(data, for: characteristic, type: .withResponse)
            peripheral.writeValue(data, for: characteristic, type: .withoutResponse)
        }
        print("did Discover Characteristics for executed")
    }
    
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        print("did Update Notification State For executed characteristic: \(characteristic.description)")
    }
}
MESHConstant.swift
import Foundation

enum MESH: String {
    case UUID = "72C90001-57A9-4D40-B746-534E22EC9F9E"
    var description: String {
        return rawValue
    }
}

コード (iOS アプリの画面関連)

ContentView.swift
import SwiftUI
import CoreBluetooth

struct ContentView: View {
    @EnvironmentObject var manager: DeviceManager
    
    var body: some View {
        VStack {
            NavigationView {
                List(manager.devices,id:\.self){ device in
                    NavigationLink {
                        DeviceDetailView(device: device)
                    } label: {
                        DeviceListView(device: device)
                    }
                }
                .navigationTitle(Text("Peripherals"))
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

GitHub

実際にアプリを実行 (YouTube)

開発した iOS アプリを MacBook Air (M1) 上で動かす

参考資料

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?