LoginSignup
1
1

More than 1 year has passed since last update.

M5Paperを使ってSwitchBot操作パネル作る

Last updated at Posted at 2023-03-31

はじめに

M5PaperはM5Stackシリーズの中で電子ペーパーを搭載したバージョンです。
ESP32が内蔵されているので、Wi-FiやBluetooth等が使えるおもしろいデバイスになっています。

※公式から借用

これを使用してSwitchBotの操作端末を作ってみます。
SwitchBotの操作には、SwitchBot API v1.1を使います。
開発言語はc++、環境はVS Codeです。

開発環境

開発環境の構築はほとんどこちらの記事を参考にさせていただきました。

追加で下記のライブラリをインポートしています。

protohaus/ESPRandom@^1.4.1
adafruit/RTClib@^2.1.1

プロジェクトの作成

上の記事で使われていたFactoryTestをベースにします。
ビルド&インストールすると、下記のような画面が出力されるはずです。
今回はHome画面を書き換えて実装したいと思います。

メイン画面:

Home画面:

実装の流れ

  • SwitchbotのトークンやdeviceID等の情報を取得する(取得方法やリクエストの方法は下記の記事を参考にしてください)

  • Home画面のUIを変更する
  • コールバック関数を登録してSwitchBot APIをリクエストする

Home画面のUI変更

完成はこれです。
今回は、寝室とリビングのシャッターのスイッチを押すボタンを4つ作りました。

Home画面はFrame_Homeというクラスで制御されているため、下記のように改修しました。

  • UI配置
    M5Paper_FactoryTestプロジェクトには、はじめからスイッチ(ON-OFF)やボタン、テキストボックスといったUIが用意されています。
    今回は、ボタンクラスのEPDGUI_Buttonを使いました。
    引数で位置とサイズを指定しています。
*.cpp
    _btn_living_shutter_up   = new EPDGUI_Button(20, 44 + 72, 228, 228);
    _btn_living_shutter_down = new EPDGUI_Button(20, 324 + 72, 228, 228);
    _btn_room_shutter_up     = new EPDGUI_Button(288, 44 + 72, 228, 228);
    _btn_room_shutter_down   = new EPDGUI_Button(288, 324 + 72, 228, 228);
  • アイコン・イメージ文字列の設定
    InitSwitchというスイッチUIを設定する関数が用意されていたので、コピペしてボタン用に改修しました。
*.cpp
    InitButton(_btn_living_shutter_up, "Open", "Living", ImageResource_home_icon_light_on_92x92);
    InitButton(_btn_living_shutter_down, "Close", "Living", ImageResource_home_icon_conditioner_off_92x92);
    InitButton(_btn_room_shutter_up, "Open", "Bedroom", ImageResource_home_icon_light_on_92x92);
    InitButton(_btn_room_shutter_down, "Close", "Bedroom", ImageResource_home_icon_conditioner_off_92x92);
*.cpp
void Frame_Home::InitButton(EPDGUI_Button *btn, String title, String subtitle, const uint8_t *img) {
    memcpy(btn->CanvasNormal()->frameBuffer(),
           ImageResource_home_button_background_228x228, 228 * 228 / 2);
    btn->CanvasNormal()->setTextSize(36);
    btn->CanvasNormal()->setTextDatum(TC_DATUM);
    btn->CanvasNormal()->drawString(title, 114, 136);
    btn->CanvasNormal()->setTextSize(26);
    btn->CanvasNormal()->drawString(subtitle, 114, 183);
    btn->CanvasNormal()->pushImage(68, 20, 92, 92, img);
}
  • コールバック関数の登録

今回は使っていませんが、下記のようにオブジェクトを登録(複数可)しておくとコールバック関数でオブジェクトを参照できるようです。
例)_btn_living_shutter_upの0番目に_btn_living_shutter_up(自分)を登録
使い道としては、ボタンが押されたときに他のボタンを無効にするなどが思い浮かびます。

*.cpp
    _btn_living_shutter_up->AddArgs(EPDGUI_Button::EVENT_RELEASED, 0, _btn_living_shutter_up);
    _btn_living_shutter_down->AddArgs(EPDGUI_Button::EVENT_RELEASED, 0, _btn_living_shutter_down);
    _btn_room_shutter_up->AddArgs(EPDGUI_Button::EVENT_RELEASED, 0, _btn_room_shutter_up);
    _btn_room_shutter_down->AddArgs(EPDGUI_Button::EVENT_RELEASED, 0, _btn_room_shutter_down);

コールバック関数をBind関数で登録しています。

*.cpp
    _btn_living_shutter_up->Bind(EPDGUI_Button::EVENT_RELEASED, btn_living_shutter_up_cb);
    _btn_living_shutter_down->Bind(EPDGUI_Button::EVENT_RELEASED, btn_living_shutter_down_cb);
    _btn_room_shutter_up->Bind(EPDGUI_Button::EVENT_RELEASED, btn_living_room_up_cb);
    _btn_room_shutter_down->Bind(EPDGUI_Button::EVENT_RELEASED, btn_living_room_down_cb);

コールバック関数たち。
Control_Switchbotクラスは前の記事を参照ください。

*.cpp
void btn_living_shutter_up_cb(epdgui_args_vector_t &args)
{
    String res;
    Control_Switchbot::getInstance()->pushSwitchBot(PRM_SWBOT_DVC_LIVING_SHUTTER_UP_ID, &res);
}

void btn_living_shutter_down_cb(epdgui_args_vector_t &args)
{
    String res;
    Control_Switchbot::getInstance()->pushSwitchBot(PRM_SWBOT_DVC_LIVING_SHUTTER_DOWN_ID, &res);
}

void btn_living_room_up_cb(epdgui_args_vector_t &args)
{
    String res;
    Control_Switchbot::getInstance()->pushSwitchBot(PRM_SWBOT_DVC_ROOM_SHUTTER_UP_ID, &res);
}

void btn_living_room_down_cb(epdgui_args_vector_t &args)
{
    String res;
    Control_Switchbot::getInstance()->pushSwitchBot(PRM_SWBOT_DVC_ROOM_SHUTTER_DOWN_ID, &res);
}

Frame_Homeクラス改修結果

frame_home.cpp
#include "frame_home.h"

#include "control_switchbot.h"

void Frame_Home::InitSwitch(EPDGUI_Switch *sw, String title, String subtitle,
                            const uint8_t *img1, const uint8_t *img2) {
    memcpy(sw->Canvas(0)->frameBuffer(),
           ImageResource_home_button_background_228x228, 228 * 228 / 2);
    sw->Canvas(0)->setTextSize(36);
    sw->Canvas(0)->setTextDatum(TC_DATUM);
    sw->Canvas(0)->drawString(title, 114, 136);
    sw->Canvas(0)->setTextSize(26);
    sw->Canvas(0)->drawString(subtitle, 114, 183);
    memcpy(sw->Canvas(1)->frameBuffer(), sw->Canvas(0)->frameBuffer(),
           228 * 228 / 2);
    sw->Canvas(0)->pushImage(68, 20, 92, 92, img1);
    sw->Canvas(1)->pushImage(68, 20, 92, 92, img2);
}

void Frame_Home::InitButton(EPDGUI_Button *btn, String title, String subtitle, const uint8_t *img) {
    memcpy(btn->CanvasNormal()->frameBuffer(),
           ImageResource_home_button_background_228x228, 228 * 228 / 2);
    btn->CanvasNormal()->setTextSize(36);
    btn->CanvasNormal()->setTextDatum(TC_DATUM);
    btn->CanvasNormal()->drawString(title, 114, 136);
    btn->CanvasNormal()->setTextSize(26);
    btn->CanvasNormal()->drawString(subtitle, 114, 183);
    btn->CanvasNormal()->pushImage(68, 20, 92, 92, img);
}

void key_home_air_adjust_cb(epdgui_args_vector_t &args) {
    int operation     = ((EPDGUI_Button *)(args[0]))->GetCustomString().toInt();
    EPDGUI_Switch *sw = ((EPDGUI_Switch *)(args[1]));
    if (sw->getState() == 0) {
        return;
    }
    int temp = sw->GetCustomString().toInt();
    char buf[10];
    if (operation == 1) {
        temp++;

    } else {
        temp--;
    }
    sprintf(buf, "%d", temp);
    sw->SetCustomString(buf);
    sprintf(buf, "%d℃", temp);
    sw->Canvas(1)->setTextSize(36);
    sw->Canvas(1)->setTextDatum(TC_DATUM);
    sw->Canvas(1)->fillRect(114 - 100, 108, 200, 38, 0);
    sw->Canvas(1)->drawString(buf, 114, 108);
    sw->Canvas(1)->pushCanvas(sw->getX(), sw->getY(), UPDATE_MODE_A2);
}

void btn_living_shutter_up_cb(epdgui_args_vector_t &args)
{
    String res;
    Control_Switchbot::getInstance()->pushSwitchBot(PRM_SWBOT_DVC_LIVING_SHUTTER_UP_ID, &res);
}

void btn_living_shutter_down_cb(epdgui_args_vector_t &args)
{
    String res;
    Control_Switchbot::getInstance()->pushSwitchBot(PRM_SWBOT_DVC_LIVING_SHUTTER_DOWN_ID, &res);
}

void btn_living_room_up_cb(epdgui_args_vector_t &args)
{
    String res;
    Control_Switchbot::getInstance()->pushSwitchBot(PRM_SWBOT_DVC_ROOM_SHUTTER_UP_ID, &res);
}

void btn_living_room_down_cb(epdgui_args_vector_t &args)
{
    String res;
    Control_Switchbot::getInstance()->pushSwitchBot(PRM_SWBOT_DVC_ROOM_SHUTTER_DOWN_ID, &res);
}

void key_home_air_state0_cb(epdgui_args_vector_t &args) {
    EPDGUI_Button *b1 = ((EPDGUI_Button *)(args[0]));
    EPDGUI_Button *b2 = ((EPDGUI_Button *)(args[1]));
    b1->SetEnable(false);
    b2->SetEnable(false);
}

void key_home_air_state1_cb(epdgui_args_vector_t &args) {
    EPDGUI_Button *b1 = ((EPDGUI_Button *)(args[0]));
    EPDGUI_Button *b2 = ((EPDGUI_Button *)(args[1]));
    b1->SetEnable(true);
    b2->SetEnable(true);
}

Frame_Home::Frame_Home(void) {
    _frame_name = "Frame_Home";

    _btn_living_shutter_up   = new EPDGUI_Button(20, 44 + 72, 228, 228);
    _btn_living_shutter_down = new EPDGUI_Button(20, 324 + 72, 228, 228);
    _btn_room_shutter_up     = new EPDGUI_Button(288, 44 + 72, 228, 228);
    _btn_room_shutter_down   = new EPDGUI_Button(288, 324 + 72, 228, 228);

    _btn_living_shutter_up->AddArgs(EPDGUI_Button::EVENT_RELEASED, 0, _btn_living_shutter_up);
    _btn_living_shutter_down->AddArgs(EPDGUI_Button::EVENT_RELEASED, 0, _btn_living_shutter_down);
    _btn_room_shutter_up->AddArgs(EPDGUI_Button::EVENT_RELEASED, 0, _btn_room_shutter_up);
    _btn_room_shutter_down->AddArgs(EPDGUI_Button::EVENT_RELEASED, 0, _btn_room_shutter_down);

    _btn_living_shutter_up->Bind(EPDGUI_Button::EVENT_RELEASED, btn_living_shutter_up_cb);
    _btn_living_shutter_down->Bind(EPDGUI_Button::EVENT_RELEASED, btn_living_shutter_down_cb);
    _btn_room_shutter_up->Bind(EPDGUI_Button::EVENT_RELEASED, btn_living_room_up_cb);
    _btn_room_shutter_down->Bind(EPDGUI_Button::EVENT_RELEASED, btn_living_room_down_cb);

    M5EPD_Canvas canvas_temp(&M5.EPD);
    canvas_temp.createRender(36);
    uint8_t language = GetLanguage();

    InitButton(_btn_living_shutter_up, "Open", "Living", ImageResource_home_icon_light_on_92x92);
    InitButton(_btn_living_shutter_down, "Close", "Living", ImageResource_home_icon_conditioner_off_92x92);
    InitButton(_btn_room_shutter_up, "Open", "Bedroom", ImageResource_home_icon_light_on_92x92);
    InitButton(_btn_room_shutter_down, "Close", "Bedroom", ImageResource_home_icon_conditioner_off_92x92);

    if (language == LANGUAGE_JA) {
        exitbtn("ホーム");
        _canvas_title->drawString("コントロールパネル", 270, 34);
    } else if (language == LANGUAGE_ZH) {
        exitbtn("主页");
        _canvas_title->drawString("控制面板", 270, 34);
    } else {
        exitbtn("Home");
        _canvas_title->drawString("Control Panel", 270, 34);
    }

    _key_exit->AddArgs(EPDGUI_Button::EVENT_RELEASED, 0, (void *)(&_is_run));
    _key_exit->Bind(EPDGUI_Button::EVENT_RELEASED, &Frame_Base::exit_cb);
}

Frame_Home::~Frame_Home(void) {    
    delete _btn_living_shutter_up;
    delete _btn_living_shutter_down;
    delete _btn_room_shutter_up;
    delete _btn_room_shutter_down;
}

int Frame_Home::init(epdgui_args_vector_t &args) {
    _is_run = 1;
    M5.EPD.Clear();
    _canvas_title->pushCanvas(0, 8, UPDATE_MODE_NONE);
    EPDGUI_AddObject(_key_exit);

    EPDGUI_AddObject(_btn_living_shutter_up);
    EPDGUI_AddObject(_btn_living_shutter_down);
    EPDGUI_AddObject(_btn_room_shutter_up);
    EPDGUI_AddObject(_btn_room_shutter_down);

    return 3;
}
frame_home.h
#ifndef _FRAME_HOME_H_
#define _FRAME_HOME_H_

#include "frame_base.h"
#include "../epdgui/epdgui.h"
#include "control_switchbot.h"

class Frame_Home : public Frame_Base {
   public:
    Frame_Home();
    ~Frame_Home();
    int init(epdgui_args_vector_t &args);
    void InitSwitch(EPDGUI_Switch *sw, String title, String subtitle,
                    const uint8_t *img1, const uint8_t *img2);
    void InitButton(EPDGUI_Button *btn, String title, String subtitle, const uint8_t *img);

   private:
    EPDGUI_Button *_btn_living_shutter_up;
    EPDGUI_Button *_btn_living_shutter_down;
    EPDGUI_Button *_btn_room_shutter_up;
    EPDGUI_Button *_btn_room_shutter_down;
    Control_Switchbot *ctrlSwbot;
};

#endif  //_FRAME_HOME_H_

動作確認

動作確認の流れは下記の通りです。

  • ビルド&インストール
  • メイン画面からWLAN画面へ遷移し、Wi-Fiを設定
  • Home画面でボタン押下

動かない場合は、”Upload and Monitor”でデバックしてください。
image.png

さいごに

M5Paper自体が白基調なので、公式からこのようなデバイスがでてもおかしくないと思いました。
Web APIさえ提供されていれば他のデバイスも操作できるので、次はルンバの清掃ボタンを追加したいです。

残作業:
・3Dプリンターで台を作って壁に設置
・初期状態では60秒でオートパワーセーブするので常時化&省電力化
・Github登録

1
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
1
1