ブログ/こばさんの wakwak 山歩き Twitter

2017-10-30 ESP32 と ダイソー Bluetooth リモコンシャッター で Lチカ

ESP32 と ダイソー Bluetooth リモコンシャッター で Lチカ

| ESP32 と ダイソー Bluetooth リモコンシャッター で Lチカを含むブックマーク

 私の手持ちスマホ 201HW が Android 4.0.4 ということで、Bluetooth LE に対応していない・・・という理由だけで Bluetooth Classic で もがいていたのですが、HCI やら L2CAP やらを初歩から読解することに疲れ果ててしまいました。

 で、その過程で気がついたのです。

ダイソー Bluetooth リモコンシャッター は 201HW では使えない

 リモコンシャッターの裏紙には全く書かれていないと思うのですが、実は Android 4.4〜 じゃないと使えないんですよね。

 未だに Classic に固執していることこそ老害の始まりなのである・・・、ということに遂に気がついたのです。


 んで、流行は Bluetooth LE、LE と言えば GATT ですが、サクッと arduino-esp32 の最新版を pull してみたら、なんか知らないうちに GATT に対応したスタックが出来上がってる雰囲気じゃないですか。


 GATT に関する予備知識が全くない私でしたが、調べていくとそれは Bluetooth Classic の時代の様式とは全く異なる、21世紀ライクに洗練されたラクチンなプロトコルであることが分かりました。


 勉強を兼ねて、ダイソー Bluetooth リモコンシャッター を HID over GATT で ESP32 に接続してみることにしました。

 ただし、先に断っておきますが、ペンディング箇所が残っており完成じゃありません

追記(2018/10/08)

 コメント欄のやりとりを経てソースに不備を発見しました。

(というか、当時は動いたはずなのだが、ライブラリが変わったせいで動かなくなった気がする)

 最初のほうの

static uint16_t GATT_HID_REPORT = 0x2a4d;

static BLEUUID GATT_HID_REPORT((uint16_t)0x2a4d);

に書き換えて下さい!


 あと、Android ボタンを Volume Down に改造した場合、notifyCallback() の pData[0] で識別可能ぽいです。

 1:iOSボタン(Volume Up)、2:Androidボタン(Volume Down)


 未改造時はライブラリ(BLERemoteService.cpp)側の不備でボタンの識別ができません。


 この辺ひっくるめて改めて記事にする予定です。

#include "BLEDevice.h"

static int GPIO_LED = 2;
static uint16_t GATT_HID = 0x1812;
static uint16_t GATT_HID_REPORT = 0x2a4d;

static BLEAddress *pServerAddress = NULL;

class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    if (advertisedDevice.haveServiceUUID() && advertisedDevice.getServiceUUID().equals(GATT_HID)) {
      advertisedDevice.getScan()->stop();

      pServerAddress = new BLEAddress(advertisedDevice.getAddress());
      Serial.print("found device:");
      Serial.println(pServerAddress->toString().c_str());
    }
  }
};

static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify)
{
  static uint8_t last_pData = 0;
  static bool sw = false;
  
  if(last_pData && !pData[0])
  {
    sw = !sw;
    digitalWrite(GPIO_LED, (!sw ? LOW : HIGH) );
  }
  
  last_pData = pData[0];
}

void setup() {
  Serial.begin(115200);
  pinMode(GPIO_LED, OUTPUT);

  BLEDevice::init("");
  BLEScan* pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(true);
  pBLEScan->start(30);
}

void loop() {
  static boolean connected = false;

  if(pServerAddress != NULL && !connected)
  {
    BLEClient* pClient = BLEDevice::createClient();
    pClient->connect(*pServerAddress);
    
    BLERemoteService* pRemoteService = pClient->getService(GATT_HID);
    if(pRemoteService)
    {
      BLERemoteCharacteristic* pRemoteCharacteristic = pRemoteService->getCharacteristic(GATT_HID_REPORT);
      pRemoteCharacteristic->registerForNotify(notifyCallback);

      Serial.print("connected to:");
      Serial.println(pRemoteCharacteristic->toString().c_str());

      for(int i=0; i<2; i++)
      {
        digitalWrite(GPIO_LED, HIGH);
        delay(100);
        digitalWrite(GPIO_LED, LOW);
        delay(400);
      }

      connected = true;
    }
  }
}

 LED は IO2 に繋げる前提です。

 違うピンを使いたいときには最初のほうの GPIO_LED を弄って下さい。


 起動したらペアリング待ちに入ります。

 30秒で待ち受け終了しますので、その間にペアリングを済ませられなかったらリセットしてやってください。

(ペアリングと言っても、認証なしの無暗号通信モードみたいですが)


 Advertising 中にデバイスの機種名も取得できるので、やろうと思えばリモコンシャッターを識別できるんですが、手抜きで 0x1812 を吹くデバイスを見つけたら直ちに繋ぎに行っちゃってますのでご注意を。


 ペアリングに成功すると LED がチカチカってします。(ON・100ms/OFF・400ms/ON・100ms)


 ダイソーのシャッターボタンを押すとモーメンタリー的に LED が点いたり消えたりします。

(下の動画では分かりにくいですが)


D

※基板は 自作のブレークアウトボード です。 


ペンディング箇所(未解決の課題)

 初めて GATT なるものに触れてから3日くらいしか経ってなくて、キーコードの取得方法が分かりません。


 今回は notifyCallback で受けたときのデータ(2バイトのうち1バイト目)に pressed / unpressed が入ってるぽい挙動だったので、それを用いて LED 制御しています。

 こいつは USB-HID の modifier とは違う気がします。


 0x1812/0x2A4B を readValue() すると、USB-HID の仕様書に書かれてる感じな 68バイト のキーマップが取れてきました。

05 0c 09 01 a1 01 85 02 09 e9 09 ea 09 e2 09 30 15 01 25 0c 75 10 95 01 81 00 c0 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 75 08 95 01 15 00 25 f4 05 07 19 00 29 f4 81 00 c0

 お目当てのキーコードを受け取るのは 0x1812/0x2a4d じゃないか?と疑って readValue() してみるのですが、00 00 としか戻って来ません。

 VolumeUP に対応するキーコードが得られるべきはずなのですが、どうやって取るのでしょうか。

 キーコードが取得できれば、2つあるボタンを識別して違う動作をさせることができるのですが。


 ちなみに 0x1812/0x2a4d の中には 0x2902 と 0x2908 な descriptor が居るのですが、こいつらを readValue() するとフリーズ(入力待ちで処理がブロック)してしまうので、きっと descriptor の中にはいないのでしょう。

 てっきり USB-HID と同じレイアウトの8バイトが手軽に降ってくると期待していたのですが見事に裏切られ。


 専門(本業)の方、是非とも教えて下さい〜!!


 ところで Boot Protocol Mode にも期待したいのですが、どうやら非対応な気がします。

(0x1812 の中には 0x2A32 も 0x2A33 も存在しないみたい)


ペンディング箇所その2

 デバイスの切断を下位の関数では検知しているぽいのですが、上位の GATT ライブラリまで切断情報が上がってきていない雰囲気がします。

 

 BLEClient あたりに connected() みたいな接続状態を監査するプロパティが欲しいところ。

 だふん外人も同じ事に遭遇してフォーラムに意見を上げてくるとと思うので、本家の対応を待つか、Fork して勝手に弄るか、はてさて。

 まぁこれは大した問題じゃないです。


(追記)2018/10/09

 ここでペンディングとしていた課題が解消されつつあります。

 続きは こちら をどうぞ

kktkkt 2018/09/10 22:24 とても参考になるブログで楽しく読ませていただいております。
この記事と同じくESP32でBLEをやりたいと思って試したのですが
コンパイルが通りません。
arduino1.8.4
arduinoESP1.0.0
ESP BLE 0.4.2
でやっているのですが、止まってしまいます。
ほかのバージョンもいくつか試してみたのですがうまく通りません。
よろしかったらバージョンをお教えいただけませんでしょうか。
これからもたくさんの楽しい記事を楽しみに待っています。いつもありがとうございます。

こばさんこばさん 2018/09/11 08:04 おはようございます。いま試してみたら

undefined reference to `BLERemoteService::getCharacteristic(unsigned short)'

ってエラー吐きますね。(メソッドがなくなった?)
本家のライブラリ(Arduino for ESP32)が大幅に変わったせいな気がします。

HID のプロフィールもサポートされたかもしれないので、当時のことは忘れて気分一新、改めてチャレンジしてみようと思います〜

kktkkt 2018/09/11 20:27 わっわっ!
ご返信ありがとうございます!
古いバージョンで試してみようと思います!
ライブラリのアップデートが激しくってこのような記事はとても大事アンド大変かと思います。
お忙しいところご返信ありがとうございます〜

こばさんこばさん 2018/09/12 10:36 ESP32自身が BLE Device(HIDキーボード) として振る舞うところは
https://github.com/nkolban/ESP32_BLE_Arduino/blob/7951347ed68313d75c367e1f2cce763cb56d1eb2/src/BLEHIDDevice.cpp
として公開されたぽいので、これを読み解いて逆のバージョンを(BLE Client 側の視点で)作り込めば、ちゃんとボタンを識別できるようになる気がします。

ちょっと時間がなくて割けないですが、なんとかしたいですね。

kktkkt 2018/09/13 20:25 お世話になっています。
いろいろ伺ってしまい申し訳ありません。
あのあといろいろなバージョンで試してみたのですが、どれもコンパイルが通らなくって、、、

In file included from C:\Users\ここは伏せます\ここは伏せます\Documents\Arduino\libraries\ESP32_BLE_Arduino-master\src/BLEClient.h:18:0,

from C:\ここは伏せます\ここは伏せます\OneDrive\Documents\Arduino\libraries\ESP32_BLE_Arduino-master\src/BLEDevice.h:19,

from C:\ここは伏せます\ここは伏せます\OneDrive\Documents\Arduino\libraries\ESP32_BLE_Arduino\examples\BLE_notify\BLE_notify.ino:23:

C:\Users\ここは伏せます\ここは伏せます\Documents\Arduino\libraries\ESP32_BLE_Arduino-master\src/BLEExceptions.h:13:2: error: #error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions."

#error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions."

って警告が出てしまいます。
こばさんがこの記事を上げられた頃のコミットを落として使ってみたんですが、だめでした。

なんかほかに理由がありそうです。

素人ながら頑張ってみたいと思います。
esp32のBLEは搭載されているもののなかなか実現例がなくって、、、
だからこそ!こばさんのような記事がキラリ!!とひかりますねーー!!!
今後ともぜひ頑張ってください!!
うまくいったらコチラにご報告します!!!

こばさんこばさん 2018/10/08 20:38 原因が分かりました。

getCharacteristic() の引数が、以前は(きっと) uint16_t を受け付けていたと思うんですが、今はダメになってるみたいで、BLEUUID を渡さないといけないみたいです。

先頭付近を修正して下さい。
(旧)
static uint16_t GATT_HID_REPORT = 0x2a4d;

(新)
static BLEUUID GATT_HID_REPORT((uint16_t)0x2a4d);

これでコンパイル通ります。
さらに、キーマップが取れずに困っていた点も解決の目処が立ちつつあります。

@coppercele さんがフォーラムに投げた質問に良い回答が来て、0x1812/0x2a4d な Characteristic が複数ある場合の考慮がライブラリ側でなされていないという不具合が判明しました。
https://github.com/nkolban/esp32-snippets/issues/660

現段階ではライブラリ側を弄る必要がありますが、もう少しするとライブラリ側で対応して下さるのではないか?という展開になりつつあります。

kktkkt 2018/10/10 13:12 こばさんこんにちは!
いろいろとありがとうございます。
チャレンジしてみたいと思います!
git に投稿してくださった@copperceleさんもありがとうごぞいました!
新しい記事も参考にしてみます!
よーしやるぞ〜

こばさんこばさん 2018/10/10 13:55 公式がいつ対応してくれるか分からないので、取り急ぎ私のほうで Fork して不具合を回避するメソッドを新設しました。
(フォーラムのやりとりで外人さんが貼り付けたコードに限りなく近い)
https://github.com/nkolban/ESP32_BLE_Arduino/compare/master...wakwak-koba:master

どうぞ参考にして下さいませ。

kktkkt 2018/10/10 22:34 こばさん
@copperceleさん
で、で、できましたーー!!!
ブログの動画のとおりうごきましたーーー
ありがとうございますーーー!!!!!
こばさんのブログをごらんの皆さんもぜひ試してみてください!!!
なかなかESP32のBLEに関する記事が少ないなかで
すごいいい記事でした!!!

個人的にはコメント欄に書き込んだ独り言がこばさんに伝わって
@copperceleさんにもご尽力いただきながら
コトが進んだことにもびっくりしました!
いまさらながらネットってすごいなって思いました。
これからも楽しい記事を期待しています!
応援しています!ありがとうございました!!!!