ダイソーのBluetoothリモコンシャッターをESP32でハックする
はじめに
ダイソーで販売されている300円のBluetoothリモコンシャッター(Remote Shutter)を、ESP32と通信してボタンイベントを受信できるようにしてみました。この企画はすでに色々な方が挑戦されていて、情報も溢れています。ここでは下記の二つの記事を参考にさせてもらいながら、ESP32で実装してみました。
- https://wakwak-koba.hatenadiary.jp/entry/20181009/p1
- https://lang-ship.com/blog/work/m5stickc-esp32-bluetooth-shutter-1-0-4/
リモコンシャッターでLチカさせる
ESP32をつかって、リモコンシャッターのボタンが押されたらLEDの点灯状態を変化させてみました。
ソースコード
こちらがそのソースコードです:
start_up.ino
#include <Arduino.h>
#include "BLEDevice.h"
#define LED_PIN 13
static uint16_t GATT_HID = 0x1812;
//static BLEUUID GATT_HID_REPORT((uint16_t) 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()); // 2a:07:98:10:33:fa
}
}
};
void updateLedState() {
static boolean ledState = true;
if (ledState == 0) {
digitalWrite(LED_PIN, LOW);
} else {
digitalWrite(LED_PIN, HIGH);
}
ledState = !ledState;
}
static void
notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
Serial.println("notifyCallback");
switch (pData[0]) {
case 0x01: // Volume Up
Serial.println("Volume Up");
updateLedState();
break;
case 0x02: // Volume Down
Serial.println("Volume Down");
updateLedState();
break;
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
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);
if(pClient->isConnected()) {
Serial.println("connected");
} else {
return;
}
//! arduino-esp32のバージョンによってはクラッシュするので注意
//! ver 2.0.2 だとここで落ちる → ver 2.0.14 または ver 3.20014.0 で動作確認済み
BLERemoteService *pRemoteService = pClient->getService(GATT_HID);
if (pRemoteService) {
// BLERemoteCharacteristic *pRemoteCharacteristic = pRemoteService->getCharacteristic(GATT_HID_REPORT);
// pRemoteCharacteristic->registerForNotify(notifyCallback);
std::map<uint16_t, BLERemoteCharacteristic*>* mapCharacteristics = pRemoteService->getCharacteristicsByHandle();
for (std::map<uint16_t, BLERemoteCharacteristic*>::iterator i = mapCharacteristics->begin(); i != mapCharacteristics->end(); ++i) {
Serial.print("connected to:");
Serial.println(i->second->getUUID().toString().c_str());
if (i->second->canNotify()) {
Serial.println(" - Add Notify");
i->second->registerForNotify(notifyCallback);
}
}
connected = true;
}
}
delay(10); // 高負荷防止
}
注意点
ソースコードをアップロードするにあたって、注意点があります。PlatformIOを使っていると思いますが、arduino-esp32のバージョンが古いとクラッシュします。ソースコード内のコメント注釈のとおり、version 2.0.2だとダメでした。version 2.0.14以降でしたら問題なさそうです。
platformio.ini
[env]
platform = https://github.com/platformio/platform-espressif32.git
board = esp32dev
framework = arduino
monitor_speed = 115200
platform_packages =
; framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32#2.0.2
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32#2.0.14
解説
BLE処理の流れ
BLE処理の流れは以下の通りです:
- デバイスのスキャン開始: setup関数でBLEスキャンが開始され、指定したサービスUUIDを持つデバイスが見つかるまで続けられます。
- デバイス発見時の処理: MyAdvertisedDeviceCallbacksのonResultコールバックが呼び出され、条件に合致するデバイスが見つかった場合、スキャンは停止され、デバイスのアドレスが保存されます。
- デバイスへの接続試行: loop関数内で、保存されたデバイスアドレスに対して接続を試みます。接続に成功すると、次のステップに進みます。
- サービスの取得と通知の購読: 接続後、GATT HIDサービスを取得し、そのサービス内の特性(Characteristic)に対して通知を購読します。このステップでは、すべての特性をループして、通知可能な特性に対してnotifyCallbackを登録します。
- 通知の受信と処理: notifyCallbackが、購読した特性から通知を受け取るたびに呼び出され、受け取ったデータに基づいてLEDの状態を切り替えます。
このスケッチは、BLEデバイスのスキャン、接続、および通知の購読という、BLE通信の基本的なプロセスを実演しています。それぞれのステップは、BLEデバイスとのインタラクションを制御し、特定のサービスや特性にアクセスするために重要です。
関数やクラスの説明
このArduinoスケッチは、BLE(Bluetooth Low Energy)を使用して特定のサービスUUID(この場合はGATT HIDサービス)を持つデバイスをスキャンし、見つけたデバイスに接続して、通知を購読するプロセスを実行します。以下に、主要なBluetooth関連のロジックを中心に解説したテーブルを示します。
関数/クラス名 | 説明 |
---|---|
MyAdvertisedDeviceCallbacks | BLEデバイスのスキャン中に呼び出されるコールバック。指定したUUIDを持つデバイスを見つけた場合、そのデバイスのアドレスを保存し、スキャンを停止します。 |
notifyCallback | BLEデバイスからの通知を受け取ったときに呼び出されるコールバック。受け取ったデータに基づいて、特定のアクション(この例ではLEDの状態の切り替え)を実行します。 |
setup | Arduinoスケッチの初期設定を行う関数。BLEデバイスの初期化、スキャンの設定、そしてスキャンの開始を行います。 |
loop | Arduinoスケッチのメインループ。特定のデバイスが見つかり、まだ接続されていない場合、そのデバイスに接続し、GATT HIDサービスを取得して、通知を購読します。 |
UUID | UUIDを通じて、BLEデバイスは特定のサービスや特性を識別し、操作を行うことができます。これにより、互換性のあるデバイス間での正確なデータ交換が可能になります。 |
BLERemoteService | BLERemoteServiceは、BLEデバイス上で提供されるサービスを表します。サービスは、一連の特性(BLERemoteCharacteristic)をグルーピングしたもので、デバイスが提供する機能や情報のカテゴリを表します。たとえば、「バッテリーサービス」は、デバイスのバッテリーレベルに関する情報を提供する特性を含むことができます。 |
BLERemoteCharacteristic | BLERemoteCharacteristicは、BLEサービスの下で定義されるデータポイントです。特性は、サービスが提供する具体的な情報や操作を表し、データの読み取り、書き込み、通知(データの変更があったときに自動的に送信される)をサポートできます。 |
まとめ
今回のBLE通信のロジック部分をライブラリ化してみました。下記の記事よりダウンロードできますので、こちらもぜひご参考ください。
関連記事
アイデアノート > IoTのワイヤレス通信