P_20150727_2126031
iPhoneからBLEで自作機器をコントロールする案件で、MBED HRM1017が売り切れで入手できなかったので代わりとなるものを探したところBlend Microなるものを見つけました。Arduino互換のBLE受信器付きマイコンボードです。マイコンはATMega32U4で3.3V/8MHz駆動となります。

ポートはD0〜D13(D4,6,7を除く)、A0〜A5、SPIが使えます。BLEの通信は、ble_write(‘a’)、ble_read()みたいな感じで何も考えずに使うことができます。

codebender
開発環境としてCodeBenderというmbed風のオンラインIDEが提供されていて、こちらもなかなか快適です。コンパイルや基板へのダウンロードは本家のIDEよりもかなり速い印象です。
ただし重大な欠点がひとつあって、コンパイル(ベリファイ)してもソースコードファイルが保存されません。他の開発環境はだいたいcompile==saveになっているので、ついつい保存しないまま終了してしまいすべて失うというミスを何度か冒しました。

***サンプル***
・BlendMicro側

//"SPI.h/Nordic_nRF8001.h/RBL_nRF8001.h" are needed in every new project
#include <SPI.h>
#include <Nordic_nRF8001.h>
#include <RBL_nRF8001.h>

const int ledPort = 0;

void setup()
{
  pinMode(ledPort, OUTPUT);
  // Init. and start BLE library.
  ble_begin();
}

void loop()
{
  while ( ble_available() )
  {
    switch(ble_read()){
      case 0:
        digitalWrite(ledPort, 0);
        break;
      case 1:
        digitalWrite(ledPort, 1);
        break;
    }
  }
  ble_do_events();
}

・iPhone側
書籍「iOSxBLE CoreBluetoothプログラミング」を参考にしました。
viewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController {
    unsigned char value;
}
@end

viewController.m

#define SERVICE_UUID  @"713D0000-503E-4C75-BA94-3148F18D941E"
#define CHARA_READ_UUID @"713D0002-503E-4C75-BA94-3148F18D941E"
#define CHARA_WRITE_UUID @"713D0003-503E-4C75-BA94-3148F18D941E"

#import "ViewController.h"
@import CoreBluetooth;

@interface ViewController ()
<CBCentralManagerDelegate, CBPeripheralDelegate>
{
    BOOL isScanning;
}
@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBPeripheral *peripheral;
@property (nonatomic, strong) CBCharacteristic *writeCharacteristic;
@property (nonatomic, strong) CBCharacteristic *readCharacteristic;

@property (weak, nonatomic) IBOutlet UIButton *scanBtn;

- (IBAction)startBtnTapped:(id)sender;
- (IBAction)ledBtnTapped:(id)sender;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    value = 0;
    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self
                                                               queue:nil];    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

// =============================================================================
#pragma mark - CBCentralManagerDelegate

- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    
    // 特に何もしない
    NSLog(@"centralManagerDidUpdateState:%ld", (long)central.state);
}

- (void)   centralManager:(CBCentralManager *)central
    didDiscoverPeripheral:(CBPeripheral *)peripheral
        advertisementData:(NSDictionary *)advertisementData
                     RSSI:(NSNumber *)RSSI
{
    NSLog(@"発見したBLEデバイス:%@", peripheral);
    if ([peripheral.name hasPrefix:@"BlendMicro"]) {
        self.peripheral = peripheral;
        // 接続開始
        [self.centralManager connectPeripheral:peripheral
                                       options:nil];
    }
}

- (void)  centralManager:(CBCentralManager *)central
    didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"接続成功!");
    peripheral.delegate = self;
    // サービス探索開始
    [peripheral discoverServices:nil];
}

- (void)        centralManager:(CBCentralManager *)central
    didFailToConnectPeripheral:(CBPeripheral *)peripheral
                         error:(NSError *)error
{
    NSLog(@"接続失敗・・・");
}


// =============================================================================
#pragma mark - CBPeripheralDelegate

// サービス発見時に呼ばれる
- (void)     peripheral:(CBPeripheral *)peripheral
    didDiscoverServices:(NSError *)error
{
    if (error) {
        NSLog(@"エラー:%@", error);
        return;
    }
    NSArray *services = peripheral.services;
    NSLog(@"%lu 個のサービスを発見!:%@", (unsigned long)services.count, services);
    for (CBService *service in services) {
        // キャラクタリスティック探索開始
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

// キャラクタリスティック発見時に呼ばれる
- (void)                      peripheral:(CBPeripheral *)peripheral
    didDiscoverCharacteristicsForService:(CBService *)service
                                   error:(NSError *)error
{
    if (error) {
        NSLog(@"エラー:%@", error);
        return;
    }
    NSArray *characteristics = service.characteristics;
    NSLog(@"%lu 個のキャラクタリスティックを発見!%@", (unsigned long)characteristics.count, characteristics);
    for (CBCharacteristic *characteristic in characteristics) {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:CHARA_READ_UUID]]) {
            self.readCharacteristic = characteristic;
            NSLog(@"CHARA_READ_UUID を発見!");
        }
        else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:CHARA_WRITE_UUID]]) {
            self.writeCharacteristic = characteristic;
            [_scanBtn setTitle:@"DISCONNECT" forState:UIControlStateNormal];
            [_scanBtn setTitleColor:[UIColor greenColor] forState:UIControlStateNormal];
            NSLog(@"CHARA_WRITE_UUID を発見!");
        }
    }
}


// データ書き込みが完了すると呼ばれる
- (void)                peripheral:(CBPeripheral *)peripheral
    didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
                             error:(NSError *)error
{
    if (error) {
        NSLog(@"Write失敗...error:%@", error);
        return;
    }
    NSLog(@"Write成功!");
}

- (IBAction)startBtnTapped:(id)sender {
    if (!isScanning) {
        isScanning = YES;
        // スキャン開始
        [self.centralManager scanForPeripheralsWithServices:nil
                                                    options:nil];
        //[sender setTitle:@"STOP" forState:UIControlStateNormal];
        [_scanBtn setTitleColor:[UIColor yellowColor] forState:UIControlStateNormal];
    } else {
        // スキャン停止
        [self.centralManager stopScan];
        if ([self.peripheral.name hasPrefix:@"BlendMicro"]) {
            [self.centralManager cancelPeripheralConnection:self.peripheral];
        }
        [sender setTitle:@"CONNECT" forState:UIControlStateNormal];
        isScanning = NO;
        [_scanBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    }
}

- (IBAction)ledBtnTapped:(id)sender {
    NSData *data;
    value = !value;
    data = [[NSData alloc] initWithBytes:&value length:1];
    [self sendBLE:data];
}

- (void)sendBLE:(NSData *)data {
    [self.peripheral writeValue:data
              forCharacteristic:self.writeCharacteristic
                           type:CBCharacteristicWriteWithoutResponse];
}
@end