post Image
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といったらこのサイトにお世話になりましょう。
上のソースコードの細かいところはこのどちらかで大体解決するはずです。


『 Swift 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。