ArduinoとBluetoothモジュールでスマホと通信

ArduinoとBluetoothモジュールでスマホと通信
ArduinoとBluetoothモジュールでスマホと通信

Arduino UnoとBluetoothモジュール(Bluefruit LE UART Friend)を使って、iPhoneとBluetooth通信してみた。この記事ではそのやり方を解説していく。

▼ ESP32を使ったBLE通信のやり方や、ペアリングの仕組みについては、こちらの記事で詳しく解説しました。

はじめに

本記事を書くにあたって、大変参考にさせてもらったBluetooth関連の書籍をご紹介します。

ArduinoとBluetoothモジュールの配線

Bluefruit Arduino UNO
DFU 未接続
GND GND
RTS 未接続
VIN 5V
RXI DIGITAL 9
TXO DIGITAL 10
CTS DIGITAL 11
MOD 未接続

BluefruitのスイッチはUART側に設定する。

Bluefruit LE UART Friend
Bluefruit LE UART Friend

Bluetoothモジュールからのメッセージをスマホで受信

スマホからBluefruitに接続する専用のアプリがあるのでインストールする。Arduinoにプログラムを書き込まなくても電源を入れれば、Bluefruitとは接続できる。

▼ iOS版

▼ Android版

Bluefruitのアプリ画面
Bluefruitのアプリ画面

今度はArduinoのIDE側の設定、サンプルコード(Adafruit_BluefruitLE_nRF51)をこちらからダウンロードする。

解凍したらフォルダ名をAdafruit_BluefruitLE_nRF51にし、Arduinoのライブラリフォルダに取り入れる。Finderの場所は、Documents → Arduino → librariesとなっている。するとArduino IDEのスケッチ例の中にAdafruit_BluefruitLE_nRF51のサンプルコードが表示される。

Bluetooth動作テストのためのArduinoスケッチ

サンプルコードを元に改造していく。適当な文字を1秒間に1回、アプリへ送信するだけの簡単なプログラムを作ってみた。

/*
  Created by Toshihiko Arai.
  https://101010.fun/iot/arduino-bluetooth.html
*/
#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                   true  // 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)
{
  delay(1000);
  ble.write("debug\n");
}

プログラムをArduinoにアップロードし、アプリでArduinoに接続してみる。動画のようにArduinoからのメッセージをBluetooth通信で受信できた。

Bluefruit LE UART Friendの情報はこちら。

iPhoneとArudinoのBluetooth通信

今度はiPhoneからArduinoへBluetoothでメッセージを送ってみよう。図のやりとりを、SwiftでCoreBlueToothを使ってプログラミングしていく。

iPhoneからArduinoへBluetoothでメッセージ送信
iPhoneからArduinoへBluetoothでメッセージ送信

Bluetooth通信のSwiftプログラム

/*
  Created by Toshihiko Arai.
  https://101010.fun/iot/arduino-bluetooth.html
*/

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)
    }
}

Bluetooth通信のArduinoスケッチ

/*
  Created by Toshihiko Arai.
  https://101010.fun/iot/arduino-bluetooth.html
*/

#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");
  }

  
}

プログラムをそれぞれ実行すると、メッセージを送受信できる。

ArduinoとiPhone間のBluetooth通信に成功
ArduinoとiPhone間のBluetooth通信に成功

BLE通信の詳しい仕組みはこちらの記事を参考に。

参考サイト

記事に関するご質問などがあればTwitterへお返事ください。
この記事で紹介した商品
人気のArduino互換機
Arduinoで人気の周辺パーツ
Arduinoのオススメ参考書

▼ Arduino初心者向きの内容となっています。ほかのArduino書籍と比べて図や説明がとてもていねいで、読みやすかったです。Arduinoで一通りのセンサーが扱えるようになります。

▼ 外国人が書いた本を翻訳したものです。この手の書籍は、目からうろこな発見をすることが多いです。

▼ Arduinoの入門書を既に読んでいる方で、次のステップを目指したい人向きの本です。C言語のプログラミングの内容が中心です。ESP32だけでなく、ふつうのArduinoにも役立つ内容でした。

関連記事