概要

Bit Trade One(株式会社ビット・トレード・ワン)さんで販売されているADBLE01Pのご紹介

製品紹介サイトからダウンロードできるiOS用のソースファイルと、手作り電極スイッチのデモ

BLEのカスタムプロファイルの操作するiOSアプリの学習にも利用可能

今回の構成(手作り電極とADBLE01P/iPodTouchとデモアプリ)

構成図

Bit Trade Oneさんで販売されているBLE静電容量式センサ(ADBLE01P)と、手作りの静電スイッチ電極と、デモ用のサンプルソースコードをビルドして、iPodTouchで動作させています。

AppStoreからダウンロードできる評価用のアプリもありますが、今回ご紹介するのはソースコードを自分でビルドして作成するアプリです。BLEのカスタムプロファイルをiPhoneで操作する学習としても利用可能です。

タッチでペッパー君が吠えるデモアプリ動作

デモアプリと静電スイッチ

箱の中に電極に接続したADBLE01Pを入れて静電スイッチにしています。

アプリは、Xcode13.0でビルドし、ソフトウェアバージョン12.5.5のiPodTouchで動作させています。だいぶ昔のプロジェクトファイルなのでエラーや警告を修正しないと、そのままではコンパイルが通りません。参考までに、Xcode 13.0(13A233)でビルドが通るSwiftのソースを後ろに載せておきます。

ちなみに、前面に出てくるワンちゃんの写真と鳴き声は、弊社代表の家で飼われているペッパー君です。

静電スイッチ電極

静電スイッチ電極は、箱の裏にアルミホイルで作成して貼り付けてあります。タッチしなくても手のひらをかざす程度でオンするように、手のひらと同等のサイズになるようにしました。

この製品のコンセプトは、このように付属の透明導電フィルムや、アルミホイルのような導電体を使用して静電センサ・スイッチを作り、AppStoreからダウンロードできる評価アプリを使用して、応答特性を確認して設計にフィードバックさせることです。静電容量センサの電極作成と言うと、難しいイメージがあるかもしれませんが、このように簡易的に作った電極でも定性的な評価や実験は可能です。プリント基板や電極フィルムを使った設計は、簡易的な実験で目途をつけた後で行う方が確実性も高くトータルの開発コストも下がるのではないかと弊社では考えております。

電極とADBLE01Pの接続

ADBLE01Pと静電スイッチ電極とは、ワニ口クリップで接続してあります。電源はモバイルバッテリーを使用しています。CR2032でも動作しますが、動作時間は半日程度です。

興味を持たれましたらADBLE01Pのオフィシャル紹介ページもご参照ください。

Xcode13.0(13A233)でビルドが通ったSwiftのサンプルソース

上のBitTradeOneさんのサイトにプロジェクトの全体があります。バージョンが古すぎて最近のXcodeではビルドが通らないので、ViewController.swiftのみを以下のファイルで置き換えてやるとこちらではビルドが通りました。

BLEのキャラクタリスティックの書き込み(Write)と読み出し(Notify)のサンプルとなっています。

ご参考まで。

//
//  ViewController.swift
//  GeomatecIoTKitDemo
//
//  Created by Appside on 2017/03/07.
//  Copyright © 2017年 Appside. All rights reserved.
//
//  2022/08/11
//  macOS Monterey 2.5
//  Xcode Version 13.0 (13A233)
 
import UIKit
import CoreBluetooth
import AVFoundation
 
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate,
    AVAudioPlayerDelegate{
    var centralManager: CBCentralManager!
    var peripheral: CBPeripheral!
    var audioPlayer: AVAudioPlayer!
    
    let APPSIDE_SERVICE_UUID = CBUUID(string:"78636976-0217-46c4-ae05-0b988b11da48")
    let APPSIDE_CSD_SETTING_CHARACTORISTIC_UUID = CBUUID(string: "e18dcd69-ca9d-4bbd-a26a-b5f31c5541c7")
    let APPSIDE_TOUCH_SWITCH_SETTING_CHARACTORISTIC_UUID = CBUUID(string: "af1258d5-796b-4aba-bb1d-da50ca36cd95")
    let APPSIDE_TOUCH_SWITCH_CHARACTORISTIC_UUID = CBUUID(string: "bf828b23-f613-4385-8ca9-cee4d79d82eb")
 
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // セントラルマネージャ初期化
        self.centralManager = CBCentralManager(delegate: self, queue: nil)
        
        let audioPath = URL(fileURLWithPath: Bundle.main.path(forResource: "pepper1 long", ofType: "wav")!)
        // 再生プレーヤーの作成
        do{
            audioPlayer = try AVAudioPlayer(contentsOf: audioPath, fileTypeHint:nil)
        }catch{
            print("Failed AVAudioPlayer Instance")
        }
        
        audioPlayer.delegate = self
        audioPlayer.prepareToPlay()
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    //
    // セントラルマネージャの状態変化取得
    //
    func centralManagerDidUpdateState(_ central: CBCentralManager){
        
        print("Start scanning.")
        self.centralManager!.scanForPeripherals(withServices: nil, options: nil)
        
    }
    //
    // Peripheralデバイスを発見
    //
    func centralManager(_ central: CBCentralManager,
                        didDiscover peripheral: CBPeripheral,
                        advertisementData: [String : Any],
                        rssi RSSI: NSNumber)
    {
        print("発見したPeripheral: \(peripheral)")
        // デバイス名がAPSDSNSから始まるデバイスの場合、接続開始
        if (peripheral.name?.hasPrefix("APSDSNS") == true) {
            self.peripheral = peripheral
            self.centralManager.connect(self.peripheral, options: nil)
        }
    }
    //
    // Peripheralデバイスへの接続が成功すると呼ばれる
    //
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        print("Connected")
        self.centralManager.stopScan()
        
        peripheral.delegate = self
        peripheral.discoverServices(nil)
    }
    //
    // ペリフェラルへの接続が失敗すると呼ばれる
    //
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        print("Fail to connect.")
    }
    //
    // ペリフェラルへの接続が切断されると呼ばれる
    // すぐにスキャン開始して再接続を始める
    //
    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        print("Disconnect.")
        print("Start scanning.")
        self.centralManager!.scanForPeripherals(withServices: nil, options: nil)
    }
    //
    // サービス発見
    //
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        
        if let error = error {
            print("Error: \(error)")
            return
        }
        
        if !((peripheral.services?.count)! > 0) {
            print("No services")
            return
        }
        
        let services = peripheral.services!
        
        print("Number of services is \(services.count). \(services)")
        
        // 所望のセンサーサービスUUIDの場合、キャラクタリスティックの探索開始
        for service in services {
            if service.uuid.isEqual(APPSIDE_SERVICE_UUID) {
                print("Searching characteristics.")
                peripheral.discoverCharacteristics(nil, for: service)
            }
        }
    }
    //
    // キャラクタリスティック発見
    //
    func peripheral(_ peripheral: CBPeripheral,
                    didDiscoverCharacteristicsFor service: CBService,
                    error: Error?)
    {
        if let error = error {
            print("Error: \(error)")
            return
        }
        
        if !((service.characteristics?.count)! > 0) {
            print("No characteristics")
            return
        }
        
        let characteristics = service.characteristics!
        
        print("Number of characteristics is \(characteristics.count). \(characteristics)")
        
        for characteristic in characteristics {
            
            // APPSIDE_TOUCH_SWITCH_CHARACTERISTIC UUIDの場合、更新通知受け取りを開始
            if characteristic.uuid.isEqual(APPSIDE_TOUCH_SWITCH_CHARACTORISTIC_UUID) {
                print("タッチスイッチ状態を読み取り")
                
                // 更新通知受け取りを開始する
                peripheral.setNotifyValue(
                    true,
                    for: characteristic)
                
            }
            // APPSIDE_TOUCH_SWITCH_SETTING_CHARACTERISTIC UUIDの場合、しきい値・ヒステリシス値を書き込む
            if characteristic.uuid.isEqual(APPSIDE_TOUCH_SWITCH_SETTING_CHARACTORISTIC_UUID) {
                print("しきい値を書き込む")
                
                // 書き込みデータ生成
                var value:[CUnsignedChar] = [0,50,0,50,0,50,0,50,5,5,5,5]
                let data = NSData(bytes: &value, length: 12)
                
                peripheral.writeValue(
                    data as Data,
                    for: characteristic,
                    type: CBCharacteristicWriteType.withResponse)
            }
            // APPSIDE_CSD_SETTING_CHARACTERISTIC UUIDの場合、感度・自動補正・強制補正設定を書き込む
            if characteristic.uuid.isEqual(APPSIDE_CSD_SETTING_CHARACTORISTIC_UUID) {
                print("感度の値を書き込む")
                
                // 書き込みデータ生成
                var value:[CUnsignedChar] = [2,2,2,2,1,1]
                let data = NSData(bytes: &value, length: 6)
                
                peripheral.writeValue(
                    data as Data,
                    for: characteristic,
                    type: CBCharacteristicWriteType.withResponse)
            }
        }
    }
    
    //
    // データ書き込み完了
    //
    func peripheral(_ peripheral: CBPeripheral,
                    didWriteValueFor characteristic: CBCharacteristic,
                    error: Error?)
    {
        if let error = error {
            print("Fail...Error: \(error), characteristic uuid: \(characteristic.uuid)")
            return
        }
        
        print("Succeed!Service uuid: \(String(describing: characteristic.service)), characteristic uuid: \(characteristic.uuid), value: \(String(describing: characteristic.value))")
    }
    
    //
    // Notify開始/停止時
    //
    func peripheral(_ peripheral: CBPeripheral,
                    didUpdateNotificationStateFor characteristic: CBCharacteristic,
                    error: Error?)
    {
        if let error = error {
            
            print("Update notification state error: \(error)")
        }
        else {
            print("Update notification state. Characteristic UUID:\(characteristic.uuid), isNotifying: \(characteristic.isNotifying)")
        }
    }
    //
    // タッチスイッチ状態が更新された
    //
    func peripheral(_ peripheral: CBPeripheral,
                    didUpdateValueFor characteristic: CBCharacteristic,
                    error: Error?)
    {
        let data = characteristic.value
        let reportData = (data! as NSData).bytes.bindMemory(to: UInt8.self, capacity: data!.count)
        
        if let error = error {
            print("Update value error: \(error)")
            return
        }
        print("データ更新! characteristic UUID: \(characteristic.uuid), value: \(String(describing: characteristic.value))")
        print(":: data: \(reportData[0])")
 
        //
        // スイッチ1がオンしたら画像表示・音声再生
        //
        if (reportData[0] & 0x01) == 0x01 {
            audioPlayer.currentTime = 136.4
            audioPlayer.play()
            print("On")
        }        
    }    
}