LoginSignup
2
1

More than 5 years have passed since last update.

macOS × BLE Nano V2でBLE通信してみた

Last updated at Posted at 2018-09-09

背景

これまでBLEを使った子機を自作する場合はmbed TY51822r3を愛用してきたのですが,どうしてもArduino環境で実装したくなり,代替案としてArduino互換機のBLE Nano V2を使ってみることにしました.これまでの経験もあり,特につまづくところなく通信できたので備忘録としてまとめます.

環境

セントラル: MacBook Pro, macOS High Sierra, IDE: Xcode9.4.1, 言語:Swift4
ペリフェラル: BLE Nano V2, IDE: Arduino

下準備

Arduino IDEの設定

  1. 環境設定の「追加のボードマネージャのURL」にライブラリのURLを追加
    URL: https://redbear.github.io/arduino/package_redbear_nRF5x_index.json
    one.png

  2. ツール->ボード->ボードマネージャを開いてRedBearLab nRF51822 Boardsをインストール
    two.png

  3. ボードの選択をBLE_Nano2に変更

  4. 書込装置をRBL_DAPLinkに変更(他のArduino機器に書き込むときに戻すのを忘れないこと)
    four.png

Xcodeプロジェクトの設定

  1. target -> Capabilities -> App Sandbox -> Hardware -> Bluetoothにチェックをいれる
  2. info.plistPrivacy - Bluetooth Peripheral Usage Descriptionを追加して何かしらの文章を登録する

ソース

今回はUUIDを以下のように決めました

Item UUID
Service 00002525-0000-1000-8000-00805F9B34FB
Characteristics 00002828-0000-1000-8000-00805F9B34FB

BLEの仕様で最初の8桁の後半4桁(2525の部分)を自由に変えられるみたいです.

BLE Nano:ペリフェラル側

BLE_Nano_Test.ino
#include <nRF5x_BLE_API.h>

#define DEVICE_NAME "BLE_Nano"
#define BUF_LEN 20

BLE ble;
Ticker ticker_task;
static uint8_t value = 0;

// UUIDの生成
static const uint8_t service_uuid[]         = {0x00, 0x00, 0x25, 0x25, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
static const uint8_t chars_uuid[]           = {0x00, 0x00, 0x28, 0x28, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
static const uint8_t service_uuid_reverse[] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x25, 0x25, 0x00, 0x00};
uint8_t chars_value[BUF_LEN] = {0};

// キャラクタリスティックの生成 今回はRead特化
GattCharacteristic characteristic(chars_uuid, chars_value, 1, BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
GattCharacteristic *chars[] = {&characteristic};
// サービスの生成
GattService service(service_uuid, chars, sizeof(chars) / sizeof(GattCharacteristic *));

// 接続された時の処理
void connectionCallBack( const Gap::ConnectionCallbackParams_t *params ) {
  digitalWrite(D13, HIGH);
  Serial.println("Connected");
}

// 接続が途絶えた時の処理
void disconnectionCallBack(const Gap::DisconnectionCallbackParams_t *params) {
  digitalWrite(D13, LOW);
  Serial.println("Disconnected, Restart advertising");
  ble.startAdvertising();
}

// 一定間隔の処理
void periodicCallback() {
  value++;
  // キャラクタリスティックの値の更新(uint8_tの配列にする)
  ble.updateCharacteristicValue(characteristic.getValueAttribute().getHandle(), (uint8_t *)&value, 1);
}

void setup() {
  pinMode(D13, OUTPUT);
  Serial.begin(9600);

  ticker_task.attach(periodicCallback, 0.01); //micro second

  ble.init();
  ble.onConnection(connectionCallBack);
  ble.onDisconnection(disconnectionCallBack);

  ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
  // ショートネームの登録
  ble.accumulateAdvertisingPayload(GapAdvertisingData::SHORTENED_LOCAL_NAME, (const uint8_t *)"Nano", sizeof("Nano") - 1);
  // 128bit対応(サービスUUIDを反転したものを送る)
  ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)service_uuid_reverse, sizeof(service_uuid_reverse));
  // デバイスネームの登録
  ble.accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, (const uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME) - 1);

  ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
  ble.addService(service);
  ble.setDeviceName((const uint8_t *)DEVICE_NAME);
  ble.setTxPower(4);
  ble.setAdvertisingInterval(160);
  ble.setAdvertisingTimeout(0);
  ble.startAdvertising();
  Serial.println("Advertising Start!");
}

void loop() {
  ble.waitForEvent();
}

macOS:セントラル側

BLEManager.swift
import Foundation
import CoreBluetooth

protocol BLEManagerDelegate {
    func updateData(_ values: [Int])
}

class BLEManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
    private var centralManager: CBCentralManager!
    private var centralFlag: Bool = false
    private var peripheral: CBPeripheral! = nil
    private var characteristic: CBCharacteristic!
    private var ticker: Timer?
    public var delegate: BLEManagerDelegate?

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

    func start() {
        // 00002525-0000-1000-8000-00805F9B34FB
        centralManager.scanForPeripherals(withServices: [CBUUID(string: "2525")], options: nil)
    }

    func stop() {
        if peripheral != nil {
            ticker?.invalidate()
            centralManager.cancelPeripheralConnection(peripheral)
        }
    }

    //Central
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        centralFlag = (central.state == CBManagerState.poweredOn)
    }

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        Swift.print(peripheral)
        if peripheral.name == "BLE_Nano" || peripheral.name == "Nano" { //Nanoが呼ばれることもある?(謎)
            self.peripheral = peripheral
            centralManager.stopScan()
            centralManager.connect(self.peripheral, options: nil)
        }
    }

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        peripheral.delegate = self
        peripheral.discoverServices([CBUUID(string: "2525")])
    }

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

    //Periferal
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let services = peripheral.services {
            for service in services {
                peripheral.discoverCharacteristics([CBUUID(string: "2828")], for: service)
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let characteristics = service.characteristics {
            for characteristic in characteristics {
                // Read可能かどうかチェック
                if (UInt8(characteristic.properties.rawValue) & UInt8(CBCharacteristicProperties.read.rawValue)) != 0 {
                    self.characteristic = characteristic
                    Swift.print("Start Communication")
                    // 一定間隔でBLE Nanoにお問い合わせ
                    ticker = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true, block: { (t) in
                        peripheral.readValue(for: self.characteristic)
                    })
                }
            }
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        if error != nil {
            Swift.print("No Data")
            return
        }
        let data: NSData = characteristic.value! as NSData
        var buffer = [UInt8](repeating: 0, count: data.length)
        data.getBytes(&buffer, length: data.length)
        let values: [Int] = buffer.map { (value) -> Int in
            return Int(value)
        }
        delegate?.updateData(values)
    }
}

参考

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