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

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

Bluefruit LE UART Friend
Bluefruit LE UART Friend

ArduinoとBluetoothモジュールの配線

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

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

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通信に成功

参考サイト

こんな商品も人気です!

1.54インチ 電子ペーパーモジュール 200x200ラズベリーパイ エンベデッドコントローラ付き E-Inkディスプレイ画面
1.54インチ 電子ペーパーモジュール 200x200ラズベリーパイ エンベデッドコントローラ付き E-Inkディスプレイ画面

バックライトがなく、電源を切っても最後のコンテンツが長時間表示される。 超低消費電力、基本的に電力はリフレッシュにのみ必要です。 SPIインターフェイス、Raspberry Pi / Arduino / Nucleoなどのコントローラーボードとの接続用。

AmazonRakuten
M5Stack 開発ボード M5Stack Core2 開発キットデュアルコア32ビット240Mhz LX6プロセッサーUIFlow, MicroPython, A r d u i n o用
M5Stack 開発ボード M5Stack Core2 開発キットデュアルコア32ビット240Mhz LX6プロセッサーUIFlow, MicroPython, A r d u i n o用

Amazon
ELEGOO Arduino用 Nanoボード V3.0 CH340/ATmega328P、Nano V3.0互換
ELEGOO Arduino用 Nanoボード V3.0 CH340/ATmega328P、Nano V3.0互換

NanoはArduinoと互換性があり、ATmega328PやCH340と同じの公式バージョンで使用しています。 これは、最小で、完全で、ブレッドボードに優しいボードです。より多くのアナログ入力ピンとオンボード+ 5V AREFジャンパを備えた(電気的に)すべてが備わっています。

Amazon
ELEGOO Arduino用の 2.8 Inches TFT ターチスクリーン カードソケット付
ELEGOO Arduino用の 2.8 Inches TFT ターチスクリーン カードソケット付

無料チュートリアル(CDに収録)、より良い価格、より良いサービス。 (技術データ付き) 2.8インチ多彩なディスプレイ液晶画面 タッチペンも付き

AmazonRakuten

Arduinoの参考書

ESP32&Arduino 電子工作 プログラミング入門
ESP32&Arduino 電子工作 プログラミング入門

電子工作ファンに人気のマイコンArduino、そしてWiFiとBluetooth内蔵でネットワーク接続しやすいESP32。それらのマイコンでプログラムを組む際に使うのが「Arduino言語」です。「電子工作は好きだけどプログラミングはよくわからない」あるいは「プログラミングの経験がある。それを活かして電子工作を楽しみたい」--そんなみなさまのための電子工作プログラミング入門が本書です。

KindleAmazonRakuten
電子部品ごとの制御を学べる! Arduino 電子工作実践講座 改訂第2版
電子部品ごとの制御を学べる! Arduino 電子工作実践講座 改訂第2版

電子パーツを自由自在に組み合わせて電子工作ができるように、Arduinoによる電子部品ごとの制御方法を詳しく解説。初心者や電子工作に躓いた人でも安心して取り組めるよう、豊富な図・写真で徹底解説。2018年発刊の「Arduino 電子工作 実践講座」の改訂版です。

KindleAmazonRakuten
Arduinoをはじめよう 第3版 (Make:PROJECTS)
Arduinoをはじめよう 第3版 (Make:PROJECTS)

本書では、開発者自らが、Arduinoの哲学、ハードウェア、ソフトウェアの基礎を解説、誰にでもできる簡単なチュートリアルを行います。

AmazonRakuten

Amazonでお得に購入するなら、Amazonギフト券がオススメ!

\Amazonギフトがお得/

コンビニ・ATM・ネットバンキングで¥5,000以上チャージすると、プライム会員は最大2.5%ポイント、通常会員は最大2%ポイントがもらえます!
Amazonギフト券

\この記事をシェアする/