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 <font color="Red">←最重要項目</font>

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

# とにかくソースコード

## mbedサイド

```cpp: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サイド

```swift: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といったら[この記事](http://qiita.com/shu223/items/78614325ce25bf7f4379)です。
mbed×BLEといったら[このサイト](http://bril-tech.blogspot.jp/2014/05/bluetoothsmartmbed-1.html)にお世話になりましょう。
上のソースコードの細かいところはこのどちらかで大体解決するはずです。