2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

スマホを介さず、SwitchBot電球が操作できるリモコンをESP32で作る

Last updated at Posted at 2024-03-26

概要

スマホを介さずにSwitchBotデバイスが操作できるSwitchBotリモコン。確かに便利そうですが、操作できる対象がSwitchBotとSwitchBotカーテンだけなのは少し残念でした。

そこで、それ以外のSwitchBotデバイスも操作できるようなリモコンを自作してみました。実はAPIが公開されているので外部から操作するのは意外と簡単です。BLE接続する必要があるため、今回はESP32を使用して作ります。
picture.jpg

開発環境

  • Windows 11 Home 22H2
  • Arduino IDE 2.3.0
  • SwitchBot 7.41.5.13

回路構成

ESP32-DevKitCを使用し、IO22にLED、IO23にスイッチを接続します。
breadboard.png

プログラム

こちらで公開されているAPIを利用します。使い方に関しては以前に投稿した記事が参考になると思うので今回は割愛します。

SwitchBot電球に接続できたらLEDが点灯し、切断されたらLEDが消灯します。また、スイッチが押される毎に所定のコマンドを送信し、SwitchBot電球の状態を切り替えます。

switchbot_bulb.ino
#include <BLEDevice.h>

#define LED_PIN 22
#define BUTTON_PIN 23

#define MAC_ADDRESS "00:00:00:00:00:00" //put the MAC address of the device

#define SERVICE_UUID "cba20d00-224d-11e6-9fb8-0002a5d5c51b" //service UUID 
#define CHARACTERISTIC_UUID_RX "cba20002-224d-11e6-9fb8-0002a5d5c51b" //characteristic UUID of the message from the terminal to the device
#define CHARACTERISTIC_UUID_TX "cba20003-224d-11e6-9fb8-0002a5d5c51b" //characteristic UUID of the message from the device to the terminal


bool doScan = false;
bool doConnect = false;
bool connectionState = false;

int buttonState = LOW;
int lastButtonState = LOW;

static BLERemoteCharacteristic* pRemoteCharacteristicRX;
static BLERemoteCharacteristic* pRemoteCharacteristicTX;
static BLEAdvertisedDevice* pDevice;


static void notifyCallback(BLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
  uint8_t response = pData[0];
  if (response == 0x01) { //OK
    Serial.println("Executed the command successfully");
  } else {
    Serial.println("Some error occurred while executing the command");
  }
}

class clientCallbacks: public BLEClientCallbacks {
  void onConnect(BLEClient* pClient) {
  }

  void onDisconnect(BLEClient* pClient) {
    connectionState = false;
    digitalWrite(LED_PIN, LOW);
    Serial.println("Disconnected from the device");
  }
};

bool connect() {
  Serial.print("Connecting to ");
  Serial.println(pDevice->getAddress().toString().c_str());
  
  BLEClient* pClient = BLEDevice::createClient();
  Serial.println(" - Created the client");

  //connect to the server
  pClient->setClientCallbacks(new clientCallbacks());
  pClient->connect(pDevice);
  if (!pClient->isConnected()) {
    Serial.println("Failed to connect to the server");
    return false;
  }
  Serial.println(" - Connected to the server");

  //obtain the reference to the service in the server
  BLERemoteService* pRemoteService = pClient->getService(BLEUUID(SERVICE_UUID));
  if (pRemoteService == nullptr) {
    Serial.println("Failed to find the service");
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found the service");

  //obtain the reference to the characteristic RX in the service of the server
  pRemoteCharacteristicRX = pRemoteService->getCharacteristic(BLEUUID(CHARACTERISTIC_UUID_RX));
  if (pRemoteCharacteristicRX == nullptr) {
    Serial.println("Failed to find the characteristic RX");
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found the characteristic RX");

  //obtain the reference to the characteristic TX in the service of the server
  pRemoteCharacteristicTX = pRemoteService->getCharacteristic(BLEUUID(CHARACTERISTIC_UUID_TX));
  if (pRemoteCharacteristicTX == nullptr) {
    Serial.println("Failed to find the characteristic TX");
    pClient->disconnect();
    return false;
  }
  Serial.println(" - Found the characteristic TX");

  //register the callback function to receive the notifications from the server
  if (pRemoteCharacteristicTX->canNotify()) {
    pRemoteCharacteristicTX->registerForNotify(notifyCallback); 
  }
  return true;
}

class advertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    BLEAddress address = advertisedDevice.getAddress();
    Serial.println(address.toString().c_str());

    if (address.equals(BLEAddress(MAC_ADDRESS))) {
      BLEDevice::getScan()->stop();
      pDevice = new BLEAdvertisedDevice(advertisedDevice);
      Serial.println("Found the target device");

      doScan = true;
      doConnect = true;
    }
  }
};

void sendCommand() {
  uint8_t command[5];

  command[0] = 0x57; //fixed value
  command[1] = 0x0F; //header
  command[2] = 0x47; //fixed value
  command[3] = 0x01; //fixed value
  command[4] = 0x03; //toggle
  
  pRemoteCharacteristicRX->writeValue(command, sizeof(command), false); //send the command
}

void setup() {
  Serial.begin(115200);
  Serial.println("Initializing...");
  BLEDevice::init("");

  pinMode(BUTTON_PIN, INPUT_PULLUP);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  
  BLEScan* pScan = BLEDevice::getScan();
  pScan->setAdvertisedDeviceCallbacks(new advertisedDeviceCallbacks());
  pScan->setActiveScan(true); //enable active scanning
  pScan->start(5, false); //start the scan to run for 5 seconds
}

void loop() {
  if (doConnect == true) {
    if (connect()) {
      connectionState = true;
      digitalWrite(LED_PIN, HIGH);
      Serial.println("Connected to the device successfully");
    } else {
      Serial.println("Some error occurred while connecting to the device");
    }
    doConnect = false;
  }

  buttonState = digitalRead(BUTTON_PIN);
  if (lastButtonState == HIGH && buttonState == LOW) {
    if (connectionState) {
      sendCommand(); //toggle the state of the bulb
    } else if (doScan) {
      BLEDevice::getScan()->start(5, false); //start the scan to run for 5 seconds
    }
  }

  lastButtonState = buttonState;
  delay(100);
}

まとめ

このように各種SwitchBotデバイスは外部から簡単に操作できます。スイッチの数などを拡張し、オリジナルのリモコンも作ることができそうです。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?