ArduinoでBluetooth通信するまで、その3

www.101010.fun

前回からのつづき、Bluetooth接続。

今回はiOSからArduinoへメッセージを投げて、さらにそのメッセージをArduinoから受け取ってみる。

f:id:araemonz:20181121164901j:plain

Swiftでこんな感じのiOSアプリを作ってみることにした。

これができるようになればBluetoothでArduinoからデータを読み込んだり、Arduinoを操作したりすることができるようになる。

f:id:araemonz:20181121165524j:plain

import UIKit
import CoreBluetooth

class ViewController: UIViewController {

    @IBOutlet weak var reciveTextView: UITextView!
    
    
    let kUARTServiceUUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UARTサービス
    let kTXCharacteristicUUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" // ペリフェラルへ送信用
    let kRXCharacteristicUUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E" // ペリフェラルからの受信用

    var centralManager: CBCentralManager!
    var peripheral: CBPeripheral!
    var serviceUUID : CBUUID!
    var kTXCBCharacteristic: CBCharacteristic!
    var kRXCBCharacteristic: CBCharacteristic!
//    var charcteristicUUID: CBUUID!
    var charcteristicUUIDs: [CBUUID]!
    
    @IBOutlet weak var sendMessageTextField: UITextField!
    @IBAction func clickedConnectButton(_ sender: Any) {
        setup()
    }
    

    @IBAction func clickedWriteButton(_ sender: Any) {
        let peripheral = self.peripheral
        if(peripheral == nil) { return }
        
        let writeData = sendMessageTextField.text!.data(using: .utf8)!
        peripheral!.writeValue(writeData, for: kTXCBCharacteristic, type: .withResponse)
        sendMessageTextField.text = ""
    }
    

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

    /// セントラルマネージャー、UUIDの初期化
    private func setup() {
        centralManager = CBCentralManager()
        centralManager.delegate = self as CBCentralManagerDelegate
//        centralManager.scanForPeripherals(withServices: nil, options: nil)

        serviceUUID = CBUUID(string: kUARTServiceUUID)
        charcteristicUUIDs = [CBUUID(string: kTXCharacteristicUUID), CBUUID(string: kRXCharacteristicUUID)]
    }

}

//MARK : - CBCentralManagerDelegate
extension ViewController: CBCentralManagerDelegate {
    
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        
        switch central.state {
            
        //電源ONを待って、スキャンする
        case CBManagerState.poweredOn:
            let services: [CBUUID] = [serviceUUID]
            centralManager?.scanForPeripherals(withServices: services,
                                               options: nil)
        default:
            break
        }
    }
    
    /// ペリフェラルを発見すると呼ばれる
    func centralManager(_ central: CBCentralManager,
                        didDiscover peripheral: CBPeripheral,
                        advertisementData: [String : Any],
                        rssi RSSI: NSNumber) {
        
        self.peripheral = peripheral
        centralManager?.stopScan()
        
        //接続開始
        central.connect(peripheral, options: nil)
    }
    
    /// 接続されると呼ばれる
    func centralManager(_ central: CBCentralManager,
                        didConnect peripheral: CBPeripheral) {
        
        peripheral.delegate = self
        peripheral.discoverServices([serviceUUID])
    }
}

//MARK : - CBPeripheralDelegate
extension ViewController: CBPeripheralDelegate {
    
    /// サービス発見時に呼ばれる
    func peripheral(_ peripheral: CBPeripheral,
                    didDiscoverServices error: Error?) {
        
        if error != nil {
            print(error.debugDescription)
            return
        }
        
        //キャリアクタリスティク探索開始
        peripheral.discoverCharacteristics(charcteristicUUIDs,
                                           for: (peripheral.services?.first)!)
    }
    
    /// キャリアクタリスティク発見時に呼ばれる
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        
        if error != nil {
            print(error.debugDescription)
            return
        }

        for characteristics in service.characteristics! {
            if(characteristics.uuid == CBUUID(string: kTXCharacteristicUUID)) {
//                peripheral.setNotifyValue(true, for: (service.characteristics?[1])!)
                self.kTXCBCharacteristic = characteristics
                addMessageToTextView("Found TX characteristics !\n")
            } else if(characteristics.uuid == CBUUID(string: kRXCharacteristicUUID)) {
                self.kRXCBCharacteristic = characteristics
                addMessageToTextView("Found RX characteristics !\n")
            }
        }
        
        if(self.kRXCBCharacteristic != nil) {
            startReciving()
        }
    }
    
    private func startReciving() {
        peripheral.setNotifyValue(true, for: kRXCBCharacteristic)
        addMessageToTextView("Start monitoring the message from Arduino.\n\n")
    }


    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        print(#function)
        if error != nil {
            print(error.debugDescription)
            return
        }
    }
    
    /// データ更新時に呼ばれる
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        
        if error != nil {
            print(error.debugDescription)
            return
        }
        updateWithData(data: characteristic.value!)
    }
    
    private func updateWithData(data : Data) {
        if let dataString = String(data: data, encoding: String.Encoding.utf8) {
            addMessageToTextView(dataString)
        }
    }
    
    private func addMessageToTextView(_ message:String) {
        reciveTextView.text = reciveTextView.text + message
        scrollToBottom(reciveTextView)
    }
    
    private func scrollToBottom(_ textView:UITextView) {
        textView.selectedRange = NSRange(location: textView.text.count, length: 0)
        textView.isScrollEnabled = true
        
        let scrollY = textView.contentSize.height - textView.bounds.height
        let scrollPoint = CGPoint(x: 0, y: scrollY > 0 ? scrollY : 0)
        textView.setContentOffset(scrollPoint, animated: true)
    }
}

Arduino側のプログラミングはこちら。

#include <string.h>
#include <Arduino.h>
#include <SPI.h>
#include "Adafruit_BLE.h"
#include "Adafruit_BluefruitLE_SPI.h"
#include "Adafruit_BluefruitLE_UART.h"


#if SOFTWARE_SERIAL_AVAILABLE
  #include <SoftwareSerial.h>
#endif

// Arduino UNOの場合この設定でうまくいった
//
#define BLUEFRUIT_SWUART_RXD_PIN       9    // Required for software serial!
#define BLUEFRUIT_SWUART_TXD_PIN       10   // Required for software serial!
#define BLUEFRUIT_UART_CTS_PIN         11   // Required for software serial!
#define BLUEFRUIT_UART_RTS_PIN         -1    // Optional, set to -1 if unused
#define BLUEFRUIT_UART_MODE_PIN        -1    // Set to -1 if unused
#define VERBOSE_MODE                   false  // If set to 'true' enables debug output

SoftwareSerial bluefruitSS = SoftwareSerial(BLUEFRUIT_SWUART_TXD_PIN, BLUEFRUIT_SWUART_RXD_PIN);

Adafruit_BluefruitLE_UART ble(bluefruitSS, BLUEFRUIT_UART_MODE_PIN,
                      BLUEFRUIT_UART_CTS_PIN, BLUEFRUIT_UART_RTS_PIN);



void error(const __FlashStringHelper*err) {
  Serial.println(err);
  while (1);
}



void setup(void)
{
  while (!Serial); // required for Flora & Micro
  delay(500);


  Serial.begin(9600);

  if ( !ble.begin(VERBOSE_MODE) )
  {
    error(F("Couldn't find Bluefruit, make sure it's in CoMmanD mode & check wiring?"));
  }
  Serial.println( F("OK!") );
}


void loop(void)
{

  int n = 0;
  int maxMessageLength = 50;
  char recivedMessage[maxMessageLength];

   while ( ble.available() ) {    
    int c = ble.read();
    // 起動時に255(0xFF)の出力をスルー
    if(c != 0xFF) {
      if(maxMessageLength > (n - 1)) {
        recivedMessage[n] = (char)c;
        n++;
      }
    }
  }

  if(n > 0) {
    recivedMessage[n] = '\0';
    ble.write("Arduino got your message is...\n");
    ble.write(recivedMessage);
    ble.write("\n");
  }

  
}

見事、対話に成功!


Successfully talked with iOS and Arduino on a Bluetooth connection!

参考

SwiftのBluetooth処理の大部分はこちらを参考にさせていただいた。

qiita.com

UUIDはbluefruitのサイトに記されている。

https://learn.adafruit.com/getting-started-with-the-nrf8001-bluefruit-le-breakout/adding-app-support

Bluetooth用語に戸惑っていたが、こちらを読めばざっくりイメージがつく。

jellyware.jp