HOME
  Security
   Software
    Hardware
  
FPGA
  CPU
   Android
    Raspberry Pi
  
nLite
  Xcode
   etc.
    ALL
  
LINK
BACK
 

2017/09/10

Raspberry Pi bleno BLEと Androidスマホで BLE GATT通信のチャットアプリを作ってみた Raspberry Pi bleno BLEと Androidスマホで BLE GATT通信のチャットアプリを作ってみた

(Bluetooth 4.0 BLE GATTを使用した超簡易チャットアプリを作った、雑談対話 APIを使用)

Tags: [Raspberry Pi], [電子工作]





● Raspberry Pi 3 Model Bを遂に購入

 Raspberry Pi3 Model B RPI2 RPI3

 大人気の CPUボードの Raspberry Piに WiFiと Bluetoothが搭載されたモデルが新発売となりました。
 以前から Raspberry Pi 2を買おうかどうか迷っていましたが、Raspberry Pi 3 Model Bの発売を機に購入を決意してラズベリアンになる事にしました。

 ※ ラズパイの OS Raspbianはバージョンが上がる毎に過去の版と OSの内部の作りが変わり、過去に書かれた製作記事(例えば Raspbian Wheezyの時代の記事)がそのままではエラーが出たりして動かない事が有ります。
 ※ 当方のホームページのラズパイ記事は全て Raspberry Pi 3 Model Bと Raspbian Jessieの組み合わせで動作確認をしております。
(ただし、将来的に新しい Raspbian OSが出た場合に、当方の Raspbian Jessieを基にした内容がそのままでは動かない可能性が有ります。)
 ※ 2017/08/16から Raspbian OSは Raspbian Jessieから Raspbian Stretchに変わりました。
 ※ 2019/06/20から Raspbian OSは Raspbian Stretchから Raspbian Busterに変わりました。

Download Raspbian for Raspberry Pi

ちなみに、歴代のバージョンと名称は
Debianコードネーム年月備考(参考)Ubuntuでの該当名称
Debian 11Bullseye2021/08/14~2021/11からラズパイにリリースFocal Fossa 20.04 LTS ?
Debian 10Buster2019/06/20~2019/06からラズパイ4対応Bionic 18.04 LTS
Debian 9Stretch2017/08/16~2018/03からラズパイ3B+対応Xenial 16.04 LTS
Debian 8Jessie2015~2016/02からラズパイ3対応Trusty 14.04 LTS
Debian 7Wheezy2013~2016
Debian 6.0Squeeze2011~2014
Debian GNU/Linux 5.0Lenny2009~2012


●ラズパイ3 + blenoと Androidスマホの BLEで超簡易チャットアプリを作成してみるテスト
 BLE GATTを使用した超簡易チャットアプリを作りたくなったので作ってみました。

 用意するもの
 ・ラズパイ3
 ・Androidスマホ BLE対応 Android OS 5.0 Lollipop以上(API レベル 21以降)

 最近覚えた ラズパイ3の Node.js + blenoで BLE周辺機器(ペリフェラル)を作成し、Androidスマホを BLEホスト(セントラル)として BLE GATT通信による超簡易チャットアプリを作成してみます。

 ラズパイ、アンドロイド共に BLEの接続フローや エラーチェック等はザル実装なので注意してください。

 ラズパイ側にはチャットボット機能としてドコモの雑談対話 APIを使用します。
 (興味の有る人は IBM Watsonや他の人工知能、対話ボットにしてみると良いかも)



●ラズパイ3側 BLE周辺機器(ペリフェラル)

# お約束の update
sudo apt-get update

# upgradeは不要
# sudo apt-get -y upgrade

# autoremove nodejsで既存の nodejsを削除する。
sudo apt-get -y autoremove nodejs

# setup Node.js v8
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
# install Node.js v8
sudo apt-get -y install nodejs

npm install bleno

npm install pigpio


●ラズパイ3側 BLE周辺機器(ペリフェラル) チャットボット側

don/mfny2015-rpi-ble
 を参考にきった貼ったで作成しました。

 チャットボットで使用している技術要素
 Node.js ble_chat.js
 ・child_processによる外部コマンドの呼び出しと標準出力の取り込み
 ・blenoによる BLEペリフェラルの実装と書き込み要求からの通知応答処理
 ・文字列を Bufferに変換 Buffer.from("文字列", 'UTF-8')
 ・雑談対話 APIを Promiseを使った非同期処理で実行する

 DialogueSample.sh シェルスクリプト
 ・コマンドライン引数の受け渡し
 ・javaで作成したアプリの実行

 DialogueSample.java
 ・ドコモの雑談対話 APIの呼び出し
 ・コマンドライン引数の受け渡し


 PiBleChat Node.js アプリソース。

サービス UUID ffe0
キャラタリスティック UUID ffe1
ディスクリプタ UUID "00002902-0000-1000-8000-00805f9b34fb"

ble_chat.js
ble_chat2.js
const execSync = require('child_process').execSync;

var Gpio = require('pigpio').Gpio,
  button = new Gpio(25, {
    mode: Gpio.INPUT,
    pullUpDown: Gpio.PUD_UP,
    edge: Gpio.EITHER_EDGE
  });

var bleno = require('bleno');
var util = require('util');


var ChatCharacteristic = function() {
    ChatCharacteristic.super_.call(this, {
      uuid: 'ffe1',
      properties: ['read', 'write', 'notify'],
      descriptors: [
         new bleno.Descriptor({
           uuid: '2901',
           value: 'ChatCharacteristic BLE'
         })
      ]
    });

    this._updateValueCallback = null;
  };
util.inherits(ChatCharacteristic, bleno.Characteristic);

ChatCharacteristic.prototype.onReadRequest = function(offset, callback) {
  console.log("read request");
  var data = new Buffer(1);
  data[0] = 0x5A;
  callback(this.RESULT_SUCCESS, data);
};

ChatCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) {
  console.log('write request: ' + data.toString('hex'));
  console.log('write request: ' + data);
  callback(this.RESULT_SUCCESS);

  console.log("onWriteRequest 1");

  // Promiseオブジェクトを生成
  // dataの内容で雑談対話 APIを呼び出す(非同期処理)
  Promise.resolve(this._updateValueCallback).then(function(updateValueCallback){

      console.log("Promise 1");

      // 非同期処理(この中は時間の掛かる処理)
      // コマンドラインの bashシェルスクリプトの呼び出し
      var result = execSync('./DialogueSample.sh \"' + data + '\"').toString();
      result = result.replace(/\r?\n/g,"");
      console.log(result);

      // 雑談対話 APIの結果 result
      var data2 = Buffer.from(result, 'UTF-8');
      if (updateValueCallback) {
        console.log("Promise callback");
        updateValueCallback(data2);
      }

      console.log("Promise 2");

      execSync('./SampleAitalk.sh \"' + result + '\"').toString();

  }).catch(function(err){

    // 雑談対話 APIの処理でエラー
    console.log("err");
  });

  console.log("onWriteRequest 2");
}

ChatCharacteristic.prototype.onSubscribe = function(maxValueSize, updateValueCallback) {
  console.log('ChatCharacteristic subscribe');
  this._updateValueCallback = updateValueCallback;

  // Debug
  button.on('interrupt', function (level) {
    console.log("button " + level);
    var data = Buffer.from("ボタン"+ level, 'UTF-8');
    updateValueCallback(data);
  });
};

var chatService = new bleno.PrimaryService({
  uuid: 'ffe0',
  characteristics: [
    new ChatCharacteristic()
  ]
});


bleno.on('stateChange', function(state) {
  console.log('on -> stateChange: ' + state);

  if (state === 'poweredOn') {
    bleno.startAdvertising('PiBleChat', [chatService.uuid]);
  } else {
    bleno.stopAdvertising();
  }
});

bleno.on('advertisingStart', function(error) {
  console.log('on -> advertisingStart: ' + (error ? 'error ' + error : 'success'));

  if (!error) {
    bleno.setServices([chatService]);
  }
});

function exit() {
  process.exit();
}
process.on('SIGINT', exit);

DialogueSample.java
    // 返却するメッセージに関する情報の出力
    System.out.println(resultData.getUtt());

chmod +x DialogueSample.sh
DialogueSample.sh
#!/bin/bash

cd ./sample
java -cp ./class:./merged.jar DialogueSample "$1"


● Android側 BLEチャットクライアント側アプリ 開発編

 BLEの GATTのペイロードは 20バイトまでとなっていますが Android 5.0以上は requestMtuで 512バイトまで拡張できます。

 ※ iOS CoreBluetoothは 37.5ms間隔で BLE通信をします。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.ne.neko.freewing.blepichatsample">

    <!-- BLE機能の使用を宣言 -->
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <!-- BLEの SCANで必要 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <!-- BLE対応端末のみ動作可能 -->
    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

 かなり省略していますが、流れは掴めるかなと。
BluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter = bluetoothManager.getAdapter();
BluetoothAdapter.startLeScan(mLeScanCallback);

BluetoothAdapter.LeScanCallback mLeScanCallback =
  onLeScan(BluetoothDevice device)
    if device.getName() == "PiBleChat" then ConnectDevice();

-- ConnectDevice();
BluetoothGatt TargetDevice.connectGatt(this, false, BluetoothGattCallback);

BluetoothGattCallback mGattCallback =
  // BLEデバイスと接続したらサービスを検索する
  onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
    if (newState == BluetoothProfile.STATE_CONNECTED)
      gatt.requestMtu(512)
      gatt.discoverServices();

  // サービスの検索結果から目的のキャラタリスティックかを比較処理
  onServicesDiscovered(BluetoothGatt gatt, int status)
    List<BluetoothGattService> gatt.getServices();
    for (BluetoothGattService service : services) //
        List<BluetoothGattCharacteristic> service.getCharacteristics();
        for (BluetoothGattCharacteristic characteristic : characteristics)
          String cuuid = characteristic.getUuid().toString();
            "0000ffe1-0000-1000-8000-00805f9b34fb".equals(cuuid)

              // 目的のキャラタリスティックなら通知を有効化
              // Characteristicの Notificationを有効化
              boolean registered = gatt.setCharacteristicNotification(characteristic, true);
              if (registered) {
                  final String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
                  BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                          UUID.fromString(CLIENT_CHARACTERISTIC_CONFIG));
                  descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                  gatt.writeDescriptor(descriptor);
              }

  // 通知の有効化のコールバックからメッセージの書き込み(送信)
  onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status)
    if (status == BluetoothGatt.GATT_SUCCESS) {
      // 送信時用に保存する
      mTargetCharacteristic = descriptor.getCharacteristic();
    }

    // 通知の有効化の応答でデバグ用にチャットメッセージを送信する
    final BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
    characteristic.setValue("あいうえ BLE");
    gatt.writeCharacteristic(characteristic);


  // 通知を受信して返信メッセージを表示する(受信)
  onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic)
    characteristic.getStringValue(0)
    //characteristic: いい感じでちゅ


  // 送信時用に保存する
  private BluetoothGattCharacteristic mTargetCharacteristic;

  private void bleSendMessage(String sendStr) {
    mTargetCharacteristic.setValue(sendStr);
    mBluetoothGatt.writeCharacteristic(mTargetCharacteristic);
  }

  // UI上の Buttonのクリックイベントで EditTextの内容をチャット送信する
  mSendButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      // クリック時の処理
      String sendStr = mSendText.getText().toString();
      bleSendMessage(sendStr);
    }
  });


 ラズパイの動作ログ出力
$ sudo node ble_chat.js
on -> stateChange: poweredOn
on -> advertisingStart: success
ChatCharacteristic subscribe
write request: e38182e38184e38186e3818820424c45
write request: あいうえ BLE
onWriteRequest 1
onWriteRequest 2
Promise 1
Promise callback
いいでちゅ
Promise 2



Tags: [Raspberry Pi], [電子工作]

●関連するコンテンツ(この記事を読んだ人は、次の記事も読んでいます)

TIの 8051マイコン内蔵の BLE CC2540/CC2541を使ってみる
TIの 8051マイコン内蔵の BLE CC2540/CC2541を使ってみる

  Bluetooth Low Energyの SensorTagや iBeacon、CC Debuggerの使い方など

Raspberry Pi 3の Bluetooth BLEで TI SensorTagと接続してセンサーの測定値を読み取る方法
Raspberry Pi 3の Bluetooth BLEで TI SensorTagと接続してセンサーの測定値を読み取る方法

  ラズパイ3の BLEと Node.jsで、TIの SensorTag CC2541DKに接続してセンサーの状態を取得

Raspberry Pi 3の gatttoolのコマンドラインで TIの SensorTagを直接操作する方法
Raspberry Pi 3の gatttoolのコマンドラインで TIの SensorTagを直接操作する方法

  ラズパイ3と gatttoolのコマンドラインで TIの SensorTagに直接接続して gatttoolの使い方を覚える

Raspberry Pi 3に Nodejs blenoをインストールして Bluetooth BLEのペリフェラルを作成する
Raspberry Pi 3に Nodejs blenoをインストールして Bluetooth BLEのペリフェラルを作成する

  ラズパイ3と Node.js blenoで Bluetooth BLEの周辺機器機能を実装する

Raspberry Pi 3の Nodejs blenoで BLE接続して GPIOで Lチカ制御やボタン状態を読み取る
Raspberry Pi 3の Nodejs blenoで BLE接続して GPIOで Lチカ制御やボタン状態を読み取る

  ラズパイ3と Node.js blenoで BLEの周辺機器機能を実装する GPIO編

Raspberry Pi 3の Python BLE pygattlibライブラリで TIの SensorTagに接続して制御する方法
Raspberry Pi 3の Python BLE pygattlibライブラリで TIの SensorTagに接続して制御する方法

  ラズパイ3と Python BLE pygattlibで TIの SensorTagのセンサーの値を取得とボタン状態の通知を受信する

Raspberry Pi 3に Bluetooth BlueZ Version 5.42 BLE
Raspberry Pi 3に Bluetooth BlueZ Version 5.42 BLE

  ラズパイで Bluetooth 4.0の BLE gatt通信を行なう TIの SensorTagや iBeacon実験など

Amazon Dash Buttonをハックして IoTボタンとして使う方法を解説
Amazon Dash Buttonをハックして IoTボタンとして使う方法を解説

  Amazon Dash Buttonをハック。ARP要求を監視、DNSハック、IPアドレスハック等。

Raspberry Piで Amazon Dash Buttonを自在にハックする方法。node-dash-button方法
Raspberry Piで Amazon Dash Buttonを自在にハックする方法。node-dash-button方法

  ラズパイ + Node.jsで Amazon Dash Buttonをハックして IoTボタンとして使う方法を解説

Raspberry Piで Amazon Dash Buttonを自在にハックする方法。tcpdump libpcap方法
Raspberry Piで Amazon Dash Buttonを自在にハックする方法。tcpdump libpcap方法

  ラズパイ + libpcap Packet Capture libraryで Amazon Dash Buttonをハックして IoTする

Raspberry Piで Amazon Dash Buttonを自在にハックする方法。Python Scapyライブラリ方法
Raspberry Piで Amazon Dash Buttonを自在にハックする方法。Python Scapyライブラリ方法

  ラズパイ + Python Scapyライブラリで ARPパケット検出からの Amazon Dash Buttonをハック

Raspberry Piで Amazon Dash Buttonを自在にハックする方法。Node.js Dasher方法
Raspberry Piで Amazon Dash Buttonを自在にハックする方法。Node.js Dasher方法

  ラズパイ + Node.js Dasherライブラリで ARPパケット検出からの Amazon Dash Buttonをハック

Raspberry Pi 3で安定して使える相性の無い最適な microSDカードの種類のまとめ
Raspberry Pi 3で安定して使える相性の無い最適な microSDカードの種類のまとめ

  ラズパイ3で安定して使える microSDカードを購入する Teamと SanDiskは絶対に買わない

Raspberry Pi 3の Linuxコンソール上で使用する各種コマンドまとめ
Raspberry Pi 3の Linuxコンソール上で使用する各種コマンドまとめ

  ラズパイの Raspbian OSのコマンドラインで使用する便利コマンド、負荷試験や CPUシリアル番号の確認方法等も

Raspberry Pi 3で GPIO端子の I2C機能を有効化する方法
Raspberry Pi 3で GPIO端子の I2C機能を有効化する方法

  ラズパイ3の GPIO端子の I2C機能を有効にして各種センサーを繋げる方法まとめ




[HOME] | [BACK]
リンクフリー(連絡不要、ただしトップページ以外は Web構成の変更で移動する場合があります)
Copyright (c) 2017 FREE WING,Y.Sakamoto
Powered by 猫屋敷工房 & HTML Generator

http://www.neko.ne.jp/~freewing/raspberry_pi/raspberry_pi_ble_bleno_android_gatt_chat_app/