BabyIoT

BabyTechよりもただのDIY

ESP32用のBLEライブラリの軽量版 -NimBLE-

SwitchBot HubをESP32で自作(1) では、BLEのライブラリがデカすぎるため、Partitionを変更しないとアプリケーションサイズがでかすぎてエラーになるという問題がありました。

少し調べてみたところ、BLEの公式ライブラリが重すぎるからどうにかしてくれという意見がEspressifのGitにも多かったようで、有志がOSSで対応ライブラリNimBLEを別途公開してくれているようです。(どうやらこの界隈では、こっちのライブラリを使うのが当たり前になっているようですね。。。)

全然本筋が進んでいないのですが、今回はこのライブラリへの差し替えを行います。

なお、ArduinoIDE用のソースと共用されているのが少し都合が悪かったため、一からプロジェクトを作り直していますので、もしかしたら一部ソースが違うところがあるかもしれません。

開発環境

BLEライブラリの差し替え

GitHub - h2zero/NimBLE-Arduino: A fork of the NimBLE library structured for compilation with Ardruino, designed for use with ESP32.

公式のBLEライブラリをこのNimBLEに差し替えます

ライブラリの導入

外部ライブラリなので、追加で導入します。

Arduinoのライブラリとして公開されているので簡単に導入可能で、"NimBLE"と検索すると出てくるとおもいます。

f:id:babyjiji:20220126184128p:plain

PlatformIOであれば、こんなかんじで導入先Projectを指定すればよいです。

f:id:babyjiji:20220126184204p:plain

ソースの変更

どうやら、基本的にはソースの改変が不要で移植できるように作られているようです(こういうライブラリをほいほい作成・公開できるようになりたいですね。。。)

Includeの差し替え

BLEのライブラリを差し替え。

//(旧)#include "BLEDevice.h"
#include <NimBLEDevice.h>

ソースの修正

私の場合、一部変更しないとビルドが通りませんでした。

BLEAdvertisedDeviceCallbacksonResultの引数が実体からポインタになっているようですので、ポインタに変えていきます。

// アドバタイズ検出時のコールバック
class advdCallback: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice* advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
      if (advertisedDevice->haveServiceUUID()) {
        String addr = advertisedDevice->getAddress().toString().c_str();
        Serial.printf("It have service addr: %s \n", advertisedDevice->getAddress().toString().c_str());
        if (addr.equalsIgnoreCase(switchBotMac)) {

          Serial.println("Found BLE");

          advertisedDevice->getScan()->stop();
          pGattServerAddress = new BLEAddress(advertisedDevice->getAddress());
          myDevice = new BLEAdvertisedDevice(*advertisedDevice);

        }
      }
    }
};

/* 旧ソース
// アドバタイズ検出時のコールバック
class advdCallback: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
      if (advertisedDevice.haveServiceUUID()) {
        String addr = advertisedDevice.getAddress().toString().c_str();
        Serial.printf("It have service addr: %s \n", advertisedDevice.getAddress().toString().c_str());
        if (addr.equalsIgnoreCase(switchBotMac)) {
          
          Serial.println("Found BLE");

          advertisedDevice.getScan()->stop();
          pGattServerAddress = new BLEAddress(advertisedDevice.getAddress());
          myDevice = new BLEAdvertisedDevice(advertisedDevice);

        }
      }
    }
};
*/

ビルド

前回同様PlatformIOでビルドします。

サイズ比較

1420102 bytes -> 1011618 bytes

と、アプリケーションサイズを7割程度まで縮小させることができ、初期Partition時のAppサイズ制限1310720bytesにも収まるようになりました。

まとめ

参照ライブラリの変更と少しのソース変更だけでビルドが通り、公式ライブラリからの移植も簡単でした。

今後にBLEライブラリを使う場合は、基本的に今回のNimBLEライブラリでよさそうですね。

ソース全文(一応)

#include <Arduino.h>
#include <WiFi.h>
#include <Espalexa.h>
#include <NimBLEDevice.h>

// prototypes
boolean connectWifi();
void InitializeBLE();
static bool connectAndSendCommand(BLEAddress pAddress, uint8_t isTurnOn);

//callback functions
void changeSwitch(uint8_t val);

/////////////////Constant values///////////////////////

//Alexa
const char* deviceName = "Bedroom light";

//wifi
const char* ssid = "WifiのSSID";
const char* password = "WifiのPW";

//Switch bot
const char* switchBotMac = "SwitchBotのMAC";
static BLEUUID SERV_SWITCHBOT("cba20d00-224d-11e6-9fb8-0002a5d5c51b");
static BLEUUID CHAR_SWITCHBOT("cba20002-224d-11e6-9fb8-0002a5d5c51b");
//command of Switch bot
#define SWITCH_BOT_MODE 1 //0 : Button, 1 : Switch

#if SWITCH_BOT_MODE == 0
static uint8_t cmdPush[3]   = {0x57, 0x01, 0x00}; //Push and Pull
#else
static uint8_t cmdPress[3]  = {0x57, 0x01, 0x01}; //Turn On
static uint8_t cmdPull[3]   = {0x57, 0x01, 0x02}; //Turn Off
#endif //SWITCH_BOT_MODE

//Grobal val
boolean wifiConnected = false;
BLEScan* pBLEScan;
static BLEAddress *pGattServerAddress;
static BLEAdvertisedDevice* myDevice;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEClient*  pClient = NULL;

Espalexa espalexa;

void setup()
{
  int i = 0;
  Serial.begin(115200);
  InitializeBLE();

  for (; i < 100; i++) {
    wifiConnected = connectWifi();
    if(wifiConnected){

      espalexa.addDevice(deviceName, changeSwitch,0); //simplest definition, default state off
      espalexa.begin();
      break;
    }
    delay(3000);
  }
  if(!wifiConnected){
    Serial.println("Cannot connect to WiFi. Please check data and reset the ESP.");
    esp_restart();
  }
}

void loop()
{
   espalexa.loop();
   delay(100);
}

//callback functions
void changeSwitch(uint8_t val) {
    Serial.print("Switch is changed to ");

    if (val) {
      Serial.print("ON");
      Serial.print(val);
    }
    else  {
      Serial.println("OFF");
    }

    connectAndSendCommand(*pGattServerAddress ,val);
}

///////////////////////////Wifi////////////////////////////

// connect to wifi – returns true if successful or false if not
boolean connectWifi(){
  boolean state = true;
  int i = 0;

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("Connecting to WiFi");

  // Wait for connection
  Serial.print("Connecting...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (i > 20){
      state = false; break;
    }
    i++;
  }
  Serial.println("");
  if (state){
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
  else {
    Serial.println("Connection failed.");
  }
  return state;
}

////////////////////////////BLE/////////////////////////

// For debug
class MyClientCallback : public BLEClientCallbacks {
    void onConnect(BLEClient* pclient) {
      Serial.println("onConnect");
    }
    void onDisconnect(BLEClient* pclient) {
      Serial.println("onDisconnect");
    }
};

// アドバタイズ検出時のコールバック
class advdCallback: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice* advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice->toString().c_str());
      if (advertisedDevice->haveServiceUUID()) {
        String addr = advertisedDevice->getAddress().toString().c_str();
        Serial.printf("It have service addr: %s \n", advertisedDevice->getAddress().toString().c_str());
        if (addr.equalsIgnoreCase(switchBotMac)) {

          Serial.println("Found BLE");

          advertisedDevice->getScan()->stop();
          pGattServerAddress = new BLEAddress(advertisedDevice->getAddress());
          myDevice = new BLEAdvertisedDevice(*advertisedDevice);

        }
      }
    }
};

// SwitchBot の GATT サーバへ接続 ~ Press コマンド送信
static bool connectAndSendCommand(BLEAddress pAddress, uint8_t isTurnOn) {
  Serial.println("connectAndSendCommand Start!");
  try {
    pClient = BLEDevice::createClient();

    Serial.println(myDevice->getAddress().toString().c_str());
    pClient->setClientCallbacks(new MyClientCallback());

    Serial.println("Connecting BLE");
    while (!pClient->connect(myDevice)) {
      Serial.println("reconnect");
      delay(1000);
    }

    // 対象サービスを得る
    Serial.println("pRemoteService");
    BLERemoteService* pRemoteService = pClient->getService(SERV_SWITCHBOT);
    if (pRemoteService == nullptr) {
      Serial.println("e:service not found");
      return false;
    }

    // 対象キャラクタリスティックを得る
    Serial.println("pRemoteCharacteristic");
    pRemoteCharacteristic = pRemoteService->getCharacteristic(CHAR_SWITCHBOT);
    if (pRemoteCharacteristic == nullptr) {
      Serial.println("e:characteristic not found");
      return false;
    }

      // キャラクタリスティックに Press コマンドを書き込む
#if SWITCH_BOT_MODE == 0
    Serial.println("Send Push");
    pRemoteCharacteristic->writeValue(cmdPush, sizeof(cmdPush), false);
#else
    Serial.println("check turnOn/off");
    if(isTurnOn){
      pRemoteCharacteristic->writeValue(cmdPress, sizeof(cmdPress), false);
      Serial.println("Send Press");
    }else{
      pRemoteCharacteristic->writeValue(cmdPull, sizeof(cmdPull), false);
      Serial.println("Send Pull");
    }
#endif //SWITCH_BOT_MODE

    delay(500); //ここが長すぎると、アレクサからエラー(デバイスの応答がない)が出るので注意
    pClient->disconnect();
    pClient = NULL;
  }
  catch (...) {
    Serial.println("Error");
    if (pClient) {
      pClient->disconnect();
      pClient = NULL;
    }
    return false;
  }

  return true;
}


void InitializeBLE(){
  // BLE 初期化
  BLEDevice::init("");
  // デバイスからのアドバタイズをスキャン
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new advdCallback());
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5,false);
}

Arduino IDEからPlatformIO IDE for VS Codeにお引越し

f:id:babyjiji:20220122070532j:plain

VS Code環境でESP32を開発

前回の記事では、Arduino IDEを使ってESP32のソース作成・ビルド・書き込みを行いましたが、個人的には見やすくて使い慣れているVS Codeで開発したい。。。

というわけで、VS Code用に前回のコードを移植していきます。

VS Code Extension

VS CodeでESP32を開発する方法としては、VS Code用のExtensionをインストールすることになるのですが、主に

の2つの選択肢があるようです。

とりあえず両方トライしてみたのですが、リリースが新しいESP32公式のEspressif IDFよりも、PlatformIO IDFの方が使いやすさ、情報量の豊富さともに優れていそうですので、今回はPlatformIO IDFを使って環境の移動を行いたいと思います。

環境構築

前提環境

  • OS : Windows 10 Home
  • VS Code
    • 拡張 : C/C++ v1.7.1 (必須ではないが、ある方が便利だと思う) ※Espressif IDF for VS codeが導入済みだと、PlatformIO IDEインストール後の挙動が少し変でしたので、これはDisableにしておいた方がよさそうです

PlatformIO IDEのインストール

サイドバーの拡張機能から、PlatformIO IDEを検索してインストールするだけです、やっぱりVS CODEはこの辺の手軽さ含めて神ツールです。

f:id:babyjiji:20220122070532j:plain

ソースの移植

Arduinoからの移植

どうやら、PlatformIO IDEでは、Arduino IDEの環境をそのまま使う機能があるようです。

f:id:babyjiji:20220122070534j:plain

左下のホームアイコンからホーム画面を開いて、Import Arduino Projectを選択。

f:id:babyjiji:20220122073624j:plain

  • バイスEsressid ESP32 Dev Moduleに選択
  • Use libraries installed by Arduino IDEにチェック
  • Arduinoで作ったプロジェクトのフォルダを開く

で、Importを押したらOK

初回プロジェクト作成時は少しLoadingに時間がかかるようですが、これだけで完了。超簡単。

プロジェクトの設定

ソースの設定が完了したら、ビルド・・・と行きたいのですが、いくつか設定しておくことがあります。

プロジェクトに含まれているplatformio.iniを設定することで、動作環境等の編集が可能です。

ボーレートの設定

monitor_speed = 115200

上記を追記すると、シリアルで正常にモニタリングできます。

Partitionの設定

前回の記事でもハマったように、ビルド時のPartitionの設定をアプリケーションに応じて正しくしておかないと、容量不足のエラーが出ます。 f:id:babyjiji:20220122070540j:plain

公式のGithubにある、PatritionのCSVファイルをダウンロードして、platformio.iniと同じ位置に置いておきます。

board_build.partitions = no_ota.csv

こんな感じでダウンロードしたCSVファイルを追記すると、Partitionの設定が反映されます(前回と同じPartition設定のNO_OTAを使います)

ビルド・書き込み

ビルド

f:id:babyjiji:20220122080759j:plain

上記画像の①のチェックマークでコンパイル・ビルドが行われます。

書き込み

②の→マークで書き込みが行われます。

共にSUCCESSが表示されていれば成功です。

まとめ

結構簡単にVS-CODE環境に移動することができました。

VS-CODEに移動したかった理由の1つはUIやInteliSenseの有効化なので、これで解決できました。

しかしながら、よりシンプル且つ機能を増やしていくことを考えると、ESP-IDFフレームワークを使った形に移植するところまで持っていきたいところです。

SwitchBotHomeの自作からだいぶ脱線してきましたが、機能モリモリにしようとすると、アプリケーションのスリム化やソースの可読性も重要になってくるので、もう少しこの辺を深堀した上で進めようと思います。 - ESP-IDFフレームワークでの開発時の違い - Arduinoフレームワーク用ライブラリの移植方法

SwitchBot HubをESP32で自作(1) -SwitchBotをAlexaで操作する-

新年に立てた目標の1つ 週に1つは記事を書くという目標がいきなり頓挫するのもマズいので、1月は身の回りの利便化のため、SwitchBot HubをESP32で自作するという内容で記事を書いていく予定です。

ターゲットと具体的プランの検討

我が家にはAmazonのEchoやEcho Dotがすでに設置してある環境となっていますが、そのうちの1部屋(寝室)では設置してあるだけで、音楽用にしか使用できていないため、もう少し部屋のIoT化による利便化を図りたいと思います。

まずは具体的なターゲットを確認して、必要な機能の洗い出しと対応方法を検討します。

ターゲット

  1. 部屋の照明(壁面スイッチ)をAlexaで操作したい
  2. 部屋の家電をAlexaで操作したい
  3. 部屋の温湿度を測定・表示したい
  4. 外観を損ねない(基盤むき出しとか、配線がゴチャつくとかはNG)
  5. できるだけ安く整えたい <- ここ重要

必要な機能

  • 物理スイッチの操作
  • 家庭内Lan(Wifi)接続によるAlexa接続
  • 赤外線による家電操作(赤外線の受信、送信、コマンド登録)
  • 温湿度センサーの搭載
  • 表示ディスプレイの搭載

対応方法

ターゲットの1~4を達成するだけであれば、SwitchBotのセットを買って一式そろえるのが一番手っ取り早いと思います。

が、SwitchBotとHub, 温度計と3点セットでAmazonのセール時に買っても1万円近くするため、肝心のできるだけ安くというところが厳しくなります。

なので、出来るだけ安く達成するため、デバイスの自作を検討していきます。

物理スイッチの検討

物理スイッチの操作についてはSwitchBotの自作をされている方もいるようですが、

物理ボタンをIoT化 – ESP32で Swich bot を自作し、スマホからスイッチをON,OFFできるようにしてみた

電源で配線が必要だったり、モーターむき出しというのは避けたいので、バッテリーをつけたりモーターつけたりとすると、安価小型おしゃれの3つを自作で達成するのはかなりハードなので、物理スイッチについてはSwitchBotをおとなしく使います。(セール時に買ったので2980円) www.amazon.co.jp

Alexa接続の検討

SwitchBotはBLEしか搭載していないため、直でAlexaに接続できず、何かしらの家庭内ネットワークに接続できる中継器を挟む必要があります。

素直にいくなら、SwithBot Hubなどの別製品が必要なのですが、そこそこの値段がするため、BLEとWifiモジュール搭載で自作IoTといえばコレというくらい広く普及し安価なESP32を使って省コスト化します。 akizukidenshi.com

国内の代理店だと1000円強しますが、AliExplessなどで買うと300~400円で買えたりします。(但し技適に注意)

ESP32に - Wifi接続 - SwitchBotの操作(BLE) - Alexaデバイス化 の機能を持たせることで、Alexaと接続し、SwitchBotを操作することができそうです。

その他機能

赤外線による家電操作(赤外線の受信、送信、コマンド登録)

温湿度センサーの搭載

表示ディスプレイの搭載

これらについては

  • 赤外線受信モジュール
  • 赤外線LED
  • 温度センサ
  • 湿度センサ
  • LCDディスプレイ をESP32に搭載してやったら最低限の機能は果たせるはず、という安易な考えで、次回以降に持ち越しとします。

SwitchBotをAlexaで操作する

先人の知恵

ESP32で同じようなことは多くの方がされているようですので、先人の知恵を コピペ参考に させて頂きます。

  • AlexaからのESP32の操作

AmazonEchoのAlexaとESP32を使って声で照明の明るさを調節することができるか基礎実験をしてみました | kohacraftのblog

  • SwithBotの操作

DSAS開発者の部屋:SwitchBot を ESP32 で遠隔操作してみた

開発環境

  • ホストマシン
  • ターゲット
    • ESP32-WROOM-DEVKIT-C-v4

手順

大まかには下記の手順で作業をすればよいです。

  • ライブラリの追加
    • Esplexa
  • ソースコード(スケッチ)の作成
  • ESP32への書き込み
  • Alexaの設定
    • Alexaアプリの設定
    • バイスの追加
  • (SwitchBotの設置)
  • 動作

細かいところは参考記事の記事等を参照していただくなりググってもらうとして、アレンジした点のみ記載しておきます。

ライブラリの追加

Arduinoのライブラリ管理からEsplexaを追加しておきます。

ソースコードの作成

動作の構成としてはこんな感じになります。

  1. 初期化
  2. BLE初期化(ライブラリの初期化とBLE接続時の動作の登録)
  3. Wifiの接続
  4. Esplexaの初期化(デバイス名・呼び出し時のアクションの登録)
  5. ループ
  6. Esplexaで待ち受け
  7. Alexaからの呼び出し
  8. Esplexaの初期化で登録した関数の呼び出し(SwitchBotの操作)

実際のソースは文末に記載しています。

これを使う際には - WifiSSID/PW - SwitchBotのMACアドレス - SwitchBotの動作モード(ボタンorスイッチ)に合わせてSWITCH_BOT_MODEの変更 - DeviceName(Alexaに表示される名称)

を使用環境に合わせてください。

ESP32への書き込み

これが少しハマりました。

ソース通りビルドすると

text section exceeds available space in board最大1310720バイトのフラッシュメモリのうち、スケッチが1403338バイト(107%)を使っています。

最大327680バイトのRAMのうち、グローバル変数が51396バイト(15%)を使っていて、ローカル変数で276284バイト使うことができます。
スケッチが大きすぎます。http://www.arduino.cc/en/Guide/Troubleshooting#size には、小さくするコツが書いてあります。
ボードESP32 Dev Moduleに対するコンパイル時にエラーが発生しました。

とエラーが出てビルドできませんでした。

どうやら、BLEのライブラリがかなり容量を食うらしく、スケッチだけで1.4MBを超えているようです。

デフォルトだと、アプリケーションの最大容量が1.28MBの設定となっているようなので、Arduinoの設定から変更します。とりあえずNO OTAを選択しました。

BLEとWifiを使うだけで容量をかなり食うというのは今後モリモリ機能追加しようと思うと、この辺のアプリケーション容量がネックになってきそうです。

Alexaの設定

Alexaでデバイスの追加を行うと、DeviceNameが表示されるはずですので、これを追加するだけです。

今回は照明用のスイッチとしてSwitchBotをつけているので、照明デバイスとして追加します。

SwitchBotの設置

こんな感じで設置してます。

動作

Alexaへの呼びかけ

「Alexa,照明をつけて」、「Alexa, 照明を消して」

でSwitchBotが動作して、照明のON/OFFをAlexa経由でできました。

照明以外のデバイスにSwitchBotの取り付ける場合には、照明以外のデバイスとして登録して、独自の名称をつけることで変更できるかと思います。

ソースコード

ESP32_Alexa.ino

#include <WiFi.h>
#include <Espalexa.h>
#include "BLEDevice.h"

// prototypes
boolean connectWifi();

//callback functions
void changeSwitch(uint8_t val);

/////////////////Constant values///////////////////////

//Alexa
const char* deviceName = "Bedroom light";

//wifi
const char* ssid = "Buffalo-D-C6CE";
const char* password = "hk6f47sue6e76";

//Switch bot
const char* switchBotMac = "F7:62:D9:3B:70:37";
static BLEUUID SERV_SWITCHBOT("cba20d00-224d-11e6-9fb8-0002a5d5c51b");
static BLEUUID CHAR_SWITCHBOT("cba20002-224d-11e6-9fb8-0002a5d5c51b");
//command of Switch bot
#define SWITCH_BOT_MODE 1 //0 : Button, 1 : Switch

#if SWITCH_BOT_MODE == 0
static uint8_t cmdPush[3]   = {0x57, 0x01, 0x00}; //Push and Pull
#else
static uint8_t cmdPress[3]  = {0x57, 0x01, 0x01}; //Turn On
static uint8_t cmdPull[3]   = {0x57, 0x01, 0x02}; //Turn Off
#endif //SWITCH_BOT_MODE

//Grobal val
boolean wifiConnected = false;
BLEScan* pBLEScan;
static BLEAddress *pGattServerAddress;
static BLEAdvertisedDevice* myDevice;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEClient*  pClient = NULL;

Espalexa espalexa;

void setup()
{
  int i = 0;
  Serial.begin(115200);
  InitializeBLE();
  
  for (; i < 100; i++) {
    wifiConnected = connectWifi();
    if(wifiConnected){
  
      espalexa.addDevice(deviceName, changeSwitch,0); //simplest definition, default state off
      espalexa.begin(); 
      break; 
    }
    delay(3000);
  }
  if(!wifiConnected){
    Serial.println("Cannot connect to WiFi. Please check data and reset the ESP.");
    esp_restart();
  }
}
 
void loop()
{
   espalexa.loop();
   delay(100);
}

//callback functions
void changeSwitch(uint8_t val) {
    Serial.print("Switch is changed to ");
    
    if (val) {
      Serial.print("ON");
      Serial.print(val);
    }
    else  {
      Serial.println("OFF");
    }
    
    connectAndSendCommand(*pGattServerAddress ,val);
}

///////////////////////////Wifi////////////////////////////

// connect to wifi – returns true if successful or false if not
boolean connectWifi(){
  boolean state = true;
  int i = 0;
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("Connecting to WiFi");

  // Wait for connection
  Serial.print("Connecting...");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    if (i > 20){
      state = false; break;
    }
    i++;
  }
  Serial.println("");
  if (state){
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }
  else {
    Serial.println("Connection failed.");
  }
  return state;
}

////////////////////////////BLE/////////////////////////

// For debug
class MyClientCallback : public BLEClientCallbacks {
    void onConnect(BLEClient* pclient) {
      Serial.println("onConnect");
    }
    void onDisconnect(BLEClient* pclient) {
      Serial.println("onDisconnect");
    }
};

// アドバタイズ検出時のコールバック
class advdCallback: public BLEAdvertisedDeviceCallbacks {
    void onResult(BLEAdvertisedDevice advertisedDevice) {
      Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
      if (advertisedDevice.haveServiceUUID()) {
        String addr = advertisedDevice.getAddress().toString().c_str();
        Serial.printf("It have service addr: %s \n", advertisedDevice.getAddress().toString().c_str());
        if (addr.equalsIgnoreCase(switchBotMac)) {
          
          Serial.println("Found BLE");

          advertisedDevice.getScan()->stop();
          pGattServerAddress = new BLEAddress(advertisedDevice.getAddress());
          myDevice = new BLEAdvertisedDevice(advertisedDevice);

        }
      }
    }
};

// SwitchBot の GATT サーバへ接続 ~ Press コマンド送信
static bool connectAndSendCommand(BLEAddress pAddress, uint8_t isTurnOn) {
  Serial.println("connectAndSendCommand Start!");
  try {
    pClient = BLEDevice::createClient();

    Serial.println(myDevice->getAddress().toString().c_str());
    pClient->setClientCallbacks(new MyClientCallback());

    Serial.println("Connecting BLE");
    while (!pClient->connect(myDevice)) {
      Serial.println("reconnect");
      delay(1000);
    }

    // 対象サービスを得る
    Serial.println("pRemoteService");
    BLERemoteService* pRemoteService = pClient->getService(SERV_SWITCHBOT);
    if (pRemoteService == nullptr) {
      Serial.println("e:service not found");
      return false;
    }

    // 対象キャラクタリスティックを得る
    Serial.println("pRemoteCharacteristic");
    pRemoteCharacteristic = pRemoteService->getCharacteristic(CHAR_SWITCHBOT);
    if (pRemoteCharacteristic == nullptr) {
      Serial.println("e:characteristic not found");
      return false;
    }

      // キャラクタリスティックに Press コマンドを書き込む
#if SWITCH_BOT_MODE == 0
    Serial.println("Send Push");    
    pRemoteCharacteristic->writeValue(cmdPush, sizeof(cmdPush), false);
#else
    Serial.println("check turnOn/off");
    if(isTurnOn){
      pRemoteCharacteristic->writeValue(cmdPress, sizeof(cmdPress), false);
      Serial.println("Send Press");    
    }else{
      pRemoteCharacteristic->writeValue(cmdPull, sizeof(cmdPull), false);
      Serial.println("Send Pull");
    }
#endif //SWITCH_BOT_MODE

    delay(500); //ここが長すぎると、アレクサからエラー(デバイスの応答がない)が出るので注意
    pClient->disconnect();
    pClient = NULL;
  }
  catch (...) {
    Serial.println("Error");
    if (pClient) {
      pClient->disconnect();
      pClient = NULL;
    }
    return false;
  }

  return true;
}


void InitializeBLE(){
  // BLE 初期化
  BLEDevice::init("");
  // デバイスからのアドバタイズをスキャン
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new advdCallback());
  pBLEScan->setActiveScan(true);
  pBLEScan->start(5,false);
}

2022を精一杯生きるための抱負

今年こそは・・・

何かと”忙しい”だとか、”明日にしよう”だとか何かと理由をつけて後回しにしていたら、色々半端で終わってしまった2021年。

  • 何かOutputせねばと始めたものの、一切更新せずに終わってしまっていたブログ・・・
  • 作ろうと思って素材はいろいろ集めたものの、まったく手を付けていない部品たち・・・
  • 外資系企業に転職が決まったものの、中学生レベルの語学力・・・

そんなわけで、今年こそは精一杯、自分の120%で生きるため、まずは取り組みの習慣化を目指して目標設定を行うことにします。

目標設定

今年1年間で達成または1年後に変わっていたいことをターゲットとして、目標を設定します。

例えば

  • ブログは週に1記事は更新する
  • 1日1時間以上の語学学習を行う
  • 体脂肪率を15%以下にする

など。

ただ、1年スパンの目標を設定しても、なかなか習慣化モチベーションの維持が難しいため、 - 目標と達成状況を他人と共有する - 目標の細分化 を行います。

仕事なんかだと、タスク管理ツールといったものを使うのは当たり前だったものの、私生活には一切取り入れておらず、漫然と生活してしまっていたため、今回はTodoist を導入して、妻とそれぞれ2022年の目標を設定して、目標と達成状況の共有を行います。todoist.com

f:id:babyjiji:20220104224203p:plain
Todoist タスク管理画面

こんな感じで、1年後に対しての目標をタスクとして登録して、そのための中期目標をサブタスク、さらにそのサブタスクとして短期目標を設定していくことで、具体的かつ、達成状況を分かりやすく目標管理が出来そうです。

各プロジェクトをほかのメンバー(妻)と共有できるため、サボると目標達成できないと、それがバレてしまうというプレッシャーにより、毎日のタスクをこなそうというモチベーションが上がります。

なお、5ユーザー, 5プロジェクトまでなら無料で利用できるため、個人目標レベルの用途であればフリーアカウントで十分そうです。

当面、2022年はTodoistを家族で運用して、精一杯一年を生きてみようと思います。

IPカメラでBabyモニター

ベビーモニター

まずはベビーモニターってなんぞやのところから改めて説明しておきます。

ベビーモニターとは、その名の通り赤ちゃんをモニタリングする装置のことなわけですが、 ベビーモニターにもいくつか種類があります。

  1. 装着タイプ
  2. 設置タイプ
  3. カメラタイプ

それぞれ簡単に紹介すると、

装着タイプ

www.amazon.co.jp

オムツや服に装着して、呼吸を測定したりおむつの状態を測定できるものもあります。 ただ、この装着タイプっていちいち取り外しするのが面倒そうだなという点で、ウチでは使ってみたことがないです。

設置タイプ

item.rakuten.co.jp

私は上記のBabysenseを使っており、子供の寝床にセンサーを敷いて鼓動チェックを行うとともに更にBabysense付属のカメラで動作を監視できるようにしています。

Babysense

布団の下に設置するだけで良いので、これを設置することによる不便さというのはほとんど無いというのがメリットだと思います。

強いて言うとセンサーを解除し忘れて抱っこすると、アラームが鳴ってうるさいというのはありますが、これは装着型でも同じかと思います。

カメラタイプ

www.amazon.co.jp

国内だとPa〇sonicだとか、安価なWifiルーターでおなじみTP〇inkだとか有名どころから数多く出てたりするんですが、基本的にモニターとニコイチだったり、特定のスマホアプリでしか利用出来ないなど、赤ちゃんの監視として使う分には問題ないのですが、呼吸のチェックなどはできず、赤ちゃんが止まっていると

"息してる?大丈夫?"

と結構心配になります。

IPカメラでBabyモニター

設置型の大きな短所として、いちいち赤ちゃんを置く場所に応じて設置場所を変えなくてはいけないという点がありますが、正直こんなんやってられず、寝床に設置しっぱなしにしてます。

そのため、センサを増設したいのですが、他の場所にも全部おいていたらそれはそれでお金が足りない。 比較的安価なカメラ型のベビーモニターだと画質も低く呼吸のチェックが出来ない。

というわけで、代替手段でIPカメラを使って呼吸のモニタリングをしようと思います。

www.nanit.com

調べてみると、Nanit社というNYにある会社がカメラによる鼓動検知のセンサを作っているようですが、これに似たものを自家製で作るのが最終ゴールになります。 (やはりこの手のBabyTech関係はアメリカが一番進んでいますね)

IPカメラの購入

使用するIPカメラとしては、とにかく”安価”というのをベースに、こちらのカメラを買いました。

www.amazon.co.jp

安いカメラを探そうとすると”ネットワークカメラ”や"ホームカメラ"が多くヒットするんですが、怪しい中国製アプリのインストールをしないと一切映像が見られず、ONVIF1にも対応していなかったりする場合が多いので、あくまで”IPカメラ"としてONVIF対応しているカメラを購入する必要があります。

"ネットワークカメラ"よりは少し高めになるかもしれませんが、Pana〇onicなど数万~数十万するのが当たり前なメーカーもあったりするので、それと比べたらこの手の中国製IPカメラはめちゃくちゃ安いですね

とりあえず設置

こういう時にも、3Dプリンターがあると固定ジグを作成して家に穴をあけずに済むので便利ですね。

PoE2で電源をとれるので、Ethernetケーブルを引っ張ってきて繋いであげるだけで家のネットワークに接続&電源ON出来ます。

画像確認

f:id:babyjiji:20201212204900p:plain
イシツブテ似の息子

とりあえずVLCで接続確認をしましたが、正直人の目で見ても呼吸の有無は判断できなさそうという。。。

もう少しカメラを近くに設置するか、ズームをかけた状態で高解像度で見ないと無理かもしれません。。。

ちなみに、上記はPCのVLCでの画像(家庭内トラブルに発展しかねないので、モザイクにしてます)ですが、Iphone用のVLCでもRTSPで再生出来たので、とりあえず監視モニターとしてはこのIPカメラでも使えそうです。

PTZ3を操作出来るIPカメラを入手して、改めてベビーモニター作成に挑戦する予定です。


  1. ネットワークカメラ製品のインターフェースの規格で、映像の配信や再生、操作など各プロトコルの標準規格。ONVIFと書いてあるカメラなら、基本的に映像の取得には困らないはず。

  2. POEハブやPOEインジェクターなどを使うことでLANケーブルで電源供給が可能。

  3. パン・チルト・ズームを操作できるIPカメラなら、赤ちゃんの置き場所に応じてズームをかけて撮ることで高解像度で画像取得出来るのではという想定

ENDER3 V2到着&組み立て

Ender3が早速到着

BLACK FRIDAYの波に乗ってEnder3 V2を公式ページで購入したわけですが、公式からのメールだと配達に9日程度かかりますとあったのですが、注文から2日で届きました。

note.com

海外サイトで購入すると、梱包がボコボコになってるなんてケースもあるようですが、ウチに届いたのはだいぶ綺麗な状態で届きました。

f:id:babyjiji:20201129150825j:plain
Ender3到着!

どこから送られてきてるのかと思いきや、まさかの住之江(大阪)。

ただ、日本郵便の追跡番号とメールに書いてあったのですが、検索してもヒットせず、結局配達してきたのは佐川急便。

ここら辺のテキトーさは流石中国企業って感じですね。

また、海外サイトから購入するとUSプラグ(3本プラグ)だったりするようですが、日本発送ということで日本仕様にしてあるようで、2本プラグの電源コードでしたので、特にアダプタ不要でコンセントにさせそうです。

早速組み立て

上記のサイトで紹介しているYoutube動画を見とけば大体うまく組み立てられそうです。

youtu.be

全部英語ですが、動画を見てるだけでもある程度はわかると思います。

細かい組み立て方とかは他のサイトや上記動画で紹介されていますので、私がハマった点と気になった点だけご紹介。

土台の結束バンド

土台部品の四隅にゴムの足がついているんですが、それでもめっちゃガタガタするので、これはおかしいとよく見てみると・・・

 f:id:babyjiji:20201129151712j:plain

本体から出ているケーブル類を固定している結束バンドが思いっきり突出してます。

なのでこれを外して新しいバンドで結束しなおしてあげました。

ただ、そもそもゴム足よりも固定用金具、バンドの方が高くなっていて、こちらが地についています。。。なんだこれ?

とりあえず、結束バンドをつけなおしたらガタつかなくなったので、違和感はあるもののとりあえずはOK。

足りないベルト長

  ベルト長が微妙に足りず、固定できないという事態が発生しました。

先ほどのガタつきの件があったので、これまた中華クオリティかと落胆しつつ、気合でベルトを伸ばそうとするも届かない。

f:id:babyjiji:20201129152201j:plain
あと少し・・・

が、どうやら私がアホなだけだったようで、フレームの窪みにベルトを通すのですが、これを片側の窪みにすべてベルトを通してしまっていました。   正しくは、フレームにベルトを巻くような形で両側に通す必要があります。そうしないと、そもそもスムーズにベルトが動きません。

これを正しく装着しなおしたところ、ちゃんとベルトが届くように。

中国製だからってすべて製品が悪いことにしようとするのは良くありませんね。

完成

  おもちゃな3Dプリンターとは違って、金属フレームで液晶画面付きだと、やっぱり高級感が違います。 

f:id:babyjiji:20201129152808j:plain
早速プリント

立体パズルの修正パーツが印刷できていなかったので、精度比較もかねて早速印刷。

ちなみに、スライサーはCuraを使って、ほぼEnder3のプロファイルを流用してGcodeを作成しました。

 

静音化ということでどれくらい静かなのか期待していたのですが、ファンの音は結構うるさいです。(ひと昔前のうるさいエアコンがついてる感じ)

ただ、モーター音やベルトによるガチャガチャ音はほとんどしないので、慣れてしまえば寝ることくらいはできそう。

今週からはまた色々とプリントが捗りそうです。

3Dプリンター再購入 ~BlackFridayの波に乗れ~

壊れたプリンター

知育パズルの作成のためにひたすら安物プリンターを酷使したところ、1週間でモーターが死にました。 安く済ませるならモーターを交換するという技もありますが、そもそもおもちゃレベルの性能なので、せっかくなので買い替えたいと思います。

ちょうど良いタイミングなBLACK FRIDAY SALE

このプリンターが壊れたタイミングでちょうどAmaz〇nでもBlack Friday/Cyber Mondayセールが始まりましたので、これは買い替えろという天命と受け取り、勢いでもう少しマシなプリンターを買っちゃいたいと思います。

巷ではEnder3シリーズが2万~3万というお手ごろ価格レンジの割にカスタム性もよく使い勝手が良いとのことなので、これを狙いたいと思います。 現状、Ender3シリーズでは、

  • Ender3-V2

  • Ender3-Pro

  • Ender3 無印(3Xというのもあるようですが、違いがイマイチ分かっていません)

の3シリーズ発売されていて、上から順に新しい(高い)ようです。

さて、どれを買おうかなとAmaz〇nで調べるも、Enderシリーズはセールになっていない様子。。。(セール品も出てましたが、パチもの臭いし安くない)

www.amazon.co.jp

www.amazon.co.jp

灯台下暗し

購入チャンスではなかったのかと、他にもYah〇o!とか、楽〇とか安いのないかなーと調べてみると、

なんとEnder3のメーカー "CREALITY" の公式販売ページでBLACK FRIDAY SALEの文字が!

Black Friday Salewww.creality3dofficial.com

Ender3 ProはAmaz〇nだとクーポン値引き後で23,000円程ですが、公式だと188$!(日本円で2万円弱ですかね)

あとは送料さえ安ければと調べたところ、なぜかV2版だけ発送元に"Japan"があり、送料は無料!

こちらは236$と5000円ちょっと高くなってしまうんですが、Proよりも静音化されていたり、ヒートベッドが改良されているという口コミもあり、 寝室で夜通し稼働させている我が環境では”静音”というのは結構大事なので、思い切ってV2の方を速攻で購入してしまいました。

ちなみに、クーポンコードで下記を入力すると、5$値引きされるので、ちょっと幸せな気分になれます。

TKY:)HYN582

あとは本体が届くのを首をながーくして待つだけですね。

AliExpressとかだと2週間とかザラですが、さすがにJapanからの発送なので数日で届いてほしい。。。