Help us understand the problem. What is going on with this article?

mbed × BLE × iOSでとりあえず通信したい人のための記事

背景

mbedでBLE使って値を送りたいぞ!でもシンプルなコードの記事がみつからねぇじゃねぇか!!
ということで四苦八苦した結果なんとか動くものができたので記事にしてみます。

どんなもの?

mbedからiOSデバイスにBLEを使って、一方的に通知形式で値を送り続けるシンプルなプログラムです。

環境

PC: MacBookPro, macOS Sierra
iOS: Xcode8.3.3, Swift3, iOS10.3.2
mbed: Switch Science mbed TY51822r3, mbed OS5.5.0 ←最重要項目

mbedのOSが古いとpc.printf()がまともに使えなかったり、そもそもBLE通信がまともにできないので注意してください。

とにかくソースコード

mbedサイド

main.cpp
#include "mbed.h"
#include "BLE.h"

#if 1 //1:シリアルモニタ表示, 0:非表示
Serial pc(USBTX, USBRX);
#define DEBUG(...) { pc.printf(__VA_ARGS__); }
#else
#define DEBUG(...)
#endif

DigitalOut led(LED1);
Ticker ticker;
BLE ble;
const static char DEVICE_NAME[] = "CustomDevice";
static Gap::ConnectionParams_t connectionParams;
uint16_t uuid_list[] = {GattService::UUID_DEVICE_INFORMATION_SERVICE};
uint8_t msg = 0;//iOSデバイスに送信する値
//ここからサービスとキャラクタリスティックの用意
static const uint8_t UUID_CHAR_DATA[] = {0xFF,0xCF,0xFC,0xCC,0xFF,0xCF,0xFC,0xCC,0xFF,0xCF,0xFC,0xCC,0xFF,0xCF,0xFC,0xCC};//なんでもいい
GattCharacteristic customCharastic(UUID_CHAR_DATA, (uint8_t *)&msg, sizeof(msg), sizeof(msg), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic *customChars[] = {&customCharastic};
GattService customService(GattService::UUID_DEVICE_INFORMATION_SERVICE, customChars, sizeof(customChars) / sizeof(GattCharacteristic *));

//BLE接続したら呼ばれるやつ
void connectionCallback(const Gap::ConnectionCallbackParams_t *params) {
    DEBUG("Connected!\n\r");
    ble.getPreferredConnectionParams(&connectionParams);
    connectionParams.minConnectionInterval = 7.5;
    connectionParams.maxConnectionInterval = 10;
    connectionParams.slaveLatency = 0;
    if (ble.gap().updateConnectionParams(params->handle, &connectionParams) != BLE_ERROR_NONE) {
        DEBUG("Failed to update\n\r");
    }
}

//BLE切断したら呼ばれるやつ
void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) {
    DEBUG("Disconnected!\n\r");
    ticker.detach();
    ble.gap().startAdvertising();
}

//周期的に呼ばれるやつ
void tickerCallback() {
    DEBUG("update\n\r");
    msg += 1;
    ble.updateCharacteristicValue(customCharastic.getValueAttribute().getHandle(), (uint8_t *)&msg, sizeof(msg));
}

//通知を開始するやつ
void updatesEnabledCallback(Gap::Handle_t handle) {
    led = 1;
    ticker.attach(&tickerCallback, 0.5);
}

//通知を停止するやつ
void updatesDisabledCallback(Gap::Handle_t handle) {
    led = 0;
    ticker.detach();
}

int main(void) {
    DEBUG("start\n\r");

    DEBUG("Initialize\n\r");
    ble.init();

    DEBUG("Setup the event handlers\n\r");
    ble.gap().onConnection(connectionCallback);
    ble.gap().onDisconnection(disconnectionCallback);
    ble.onUpdatesEnabled(updatesEnabledCallback);
    ble.onUpdatesDisabled(updatesDisabledCallback);

    DEBUG("Advertising payload\n\r");
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid_list, sizeof(uuid_list));
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof((const uint8_t *)DEVICE_NAME));
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
    ble.gap().setAdvertisingInterval(160); //100ms;
    ble.gap().startAdvertising();

    DEBUG("Add service\n\r");
    ble.gattServer().addService(customService);

    while (true) {
        ble.waitForEvent();
    }
}

iOSサイド

ViewController.swift
import UIKit
import CoreBluetooth

class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {

    var centralManager: CBCentralManager!
    var peripheral: CBPeripheral!
    var characteristic: CBCharacteristic!

    override func viewDidLoad() {
        super.viewDidLoad()
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        print ("state: \(central.state)")
    }

    //接続開始(ボタンは用意する)
    @IBAction func start(_ sender: UIButton) {
        centralManager.scanForPeripherals(withServices: nil, options: nil)
    }

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        if peripheral.name != nil {
            if peripheral.name == "デバイスの名前(接続時に表示されるやつ)" {
                print(peripheral.name!)
                self.peripheral = peripheral
                self.centralManager.connect(self.peripheral, options: nil)
                //ここで少しタイムラグ挟まないと通信できない
                DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: { 
                    self.peripheral.delegate = self
                    self.peripheral.discoverServices(nil)
                })
            }
        }
    }

    //接続終了(ボタンは用意する)
    @IBAction func stop(_ sender: UIButton) {
        centralManager.stopScan()
        self.peripheral.setNotifyValue(false, for: self.characteristic)
    }

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        print("Connect success!")
    }

    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        print("Connect failed...")
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        guard let services = peripheral.services else {
            print("error")
            return
        }
        for obj in services {
            peripheral.discoverCharacteristics(nil, for: obj);
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let characteristics = service.characteristics {
            for char in characteristics {
                if char.uuid.uuidString == "FFCFFCCC-FFCF-FCCC-FFCF-FCCCFFCFFCCC" {
                    self.characteristic = char
                    self.peripheral.setNotifyValue(true, for: self.characteristic)
                }
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        //正直なくてもいい
    }

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        if error != nil {
            print("no data")
            return
        }
        let value: Int = characteristic.value!.withUnsafeBytes { (p: UnsafePointer<Int>) -> Int in
            return p.pointee
        }
        print("value: \(String(describing: value))")
    }
}

参考

iOS×BLEといったらこの記事です。
mbed×BLEといったらこのサイトにお世話になりましょう。
上のソースコードの細かいところはこのどちらかで大体解決するはずです。

Kyome
火星に住む犬です。 趣味でmacOS アプリを開発しています。
https://kyome.io
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away