最近寒いのでカーテンを遮光・遮熱のものに変えた。 遮熱のほうはあまり実感がないが遮光のほうはばっちりで、 昼間でもカーテンを閉めれば部屋がかなり暗くなる。

寝てる間は暗くてよいのだが、 朝になっても明るさで自然に目が覚めるということがなくなった。 時計を見ないと大まかな時間すら分からないのは思いの外不便だ。 そこで、以前購入した M5StickC を使って 毎朝自動で照明を点灯させることを思いついた。

使用する照明器具とリモコン

私の部屋の照明はNECライティング(現ホタルクス)の LEDシーリングライトHLDX0801 を使用している。 昨年買い換えたのだが、 これはシーリングライトとしてはかなりの薄型で、 引掛シーリングと合わせても高さ6cmくらいしかない。 以前使用していたものは高さ15cmほどあったため、 圧迫感がなくなり気に入っている。 唯一の不満点はリモコンが付属していないことで、別途 RE0206 を購入し使用している。

リモコンの信号解析

まずは点灯時に RE0206 から発信される赤外線信号を調べるため Grove 接続の IR ユニット を購入した。 M5StickC 単体でも赤外線の発信はできるが、受信はできないため、 リモコンの信号を調べる場合は別途購入する必要がある。

M5StickC/M5Stack Fireで赤外線リモコンを作ろうと四苦八苦したのでメモ - カワリモノ息子の技術メモ的な〜 を参考に Arduino IDE から IRremoteESP8266 をインストールし、スケッチ例の IRvecvDumpV3 を開く。 kRecvPin を 33 に変更して M5StickC へ書き込んだあと、 IR ユニットへ向けてリモコンのボタンを押すと シリアルモニタへ信号が表示された。

赤外線リモコンの信号にはいくつか種類があるらしいが、 NEC製品なので当然NECフォーマットだった。

スケッチの作成

M5StickC で設定時刻に赤外線信号を送信するスケッチを作成した。

saasan/m5stickc-ir-timer: M5StickCで設定時刻に赤外線信号を送信する

#include <M5StickC.h>
#include <WiFi.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include "wifi-ssid.h"

// -----------------------------------------------------------------------------
// 定数
// -----------------------------------------------------------------------------
// 赤外線LEDのピン番号
// M5StickC内蔵の赤外線LEDを使用する場合は9
// Grove接続のIRユニットを使用する場合は32
const uint8_t IR_SEND_PIN = 9;
// 赤外線送信するデータ
const uint64_t IR_SEND_DATA = 0x41B6659A;
// GMTからの時間差(秒)
const long JST = 9 * 60 * 60;
// NTPサーバ
const char *NTP_SERVER = "ntp.nict.jp";
// 電源ボタンが1秒未満押された
const uint8_t AXP_WAS_PRESSED = 2;
// ボタンが長押しされたと判定する時間(ms)
const uint32_t BUTTON_PRESSED_MS = 500;
// 最後にボタンを押してから画面を省電力にするまでの時間(ms)
const unsigned long SCREEN_OFF_MS = 3000;
// 通常時の画面輝度
const uint8_t SCREEN_ON_BRIGHTNESS = 12;
// 省電力時の画面輝度
const uint8_t SCREEN_OFF_BRIGHTNESS = 8;

// -----------------------------------------------------------------------------
// 変数
// -----------------------------------------------------------------------------
// IRremoteESP8266のIRsendクラス
IRsend irsend(IR_SEND_PIN);
// 現在時刻
struct tm now;
// 赤外線送信する時刻の時間
int timer_hour = 7;
// 赤外線送信する時刻の分
int timer_min = 0;
// 赤外線送信済みならtrue
bool ir_sent = false;
// 最後にボタンが押された時間
unsigned long button_pressed_millis = 0;

// -----------------------------------------------------------------------------
// 関数
// -----------------------------------------------------------------------------
// 現在時刻を表示
void showCurrentTime() {
    char message[50];

    sprintf(
        message,
        " now:\n  %04d/%02d/%02d %02d:%02d:%02d\n timer:\n  %02d:%02d",
        now.tm_year + 1900,
        now.tm_mon + 1,
        now.tm_mday,
        now.tm_hour,
        now.tm_min,
        now.tm_sec,
        timer_hour,
        timer_min);

    M5.Lcd.setCursor(0, 0);
    M5.Lcd.print(message);
}

// 無線LAN接続
void connectWiFi(const char *ssid, const char *passphrase) {
    M5.Lcd.printf("Connecting to %s", ssid);
    WiFi.begin(ssid, passphrase);
    while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        M5.Lcd.print(".");
    }
    M5.Lcd.print("\nconnected!");
    delay(500);
    M5.Lcd.fillScreen(BLACK);
}

// 画面の輝度を上げる
void screenOn() {
    M5.Axp.ScreenBreath(SCREEN_ON_BRIGHTNESS);
    button_pressed_millis = millis();
}

// 画面の輝度を下げる
void screenOff() {
    M5.Axp.ScreenBreath(SCREEN_OFF_BRIGHTNESS);
}

void setup() {
    M5.begin();

    // 画面の表示設定
    M5.Lcd.setRotation(1);
    M5.Lcd.setTextFont(2);
    // ピンモードの設定
    pinMode(IR_SEND_PIN, OUTPUT);
    // 無線LANへ接続
    connectWiFi(WIFI_SSID, WIFI_PASSPHRASE);
    // NTPの設定
    configTime(JST, 0, NTP_SERVER);

    button_pressed_millis = millis();
}

void loop() {
    M5.update();

    // ボタンAが押されたらアラーム時刻の時間を変更
    if (M5.BtnA.wasPressed() || M5.BtnA.pressedFor(BUTTON_PRESSED_MS)) {
        timer_hour++;
        if (timer_hour > 23) timer_hour = 0;

        screenOn();
    }
    // ボタンBが押されたらアラーム時刻の分を変更
    if (M5.BtnB.wasPressed() || M5.BtnB.pressedFor(BUTTON_PRESSED_MS)) {
        timer_min++;
        if (timer_min > 59) timer_min = 0;

        screenOn();
    }
    // 電源ボタンが押されたらリセット
    if (M5.Axp.GetBtnPress() == AXP_WAS_PRESSED) {
        esp_restart();
    }

    // 現在時刻を取得
    getLocalTime(&now);
    showCurrentTime();

    // 最後にボタンを押してから時間が経過している場合は画面を省電力化
    if ((millis() - button_pressed_millis) > SCREEN_OFF_MS) {
        screenOff();
    }

    if (now.tm_hour == timer_hour && now.tm_min == timer_min && now.tm_sec == 0) {
        // 赤外線送信していなければ電源ON信号を送信
        if (!ir_sent) {
            irsend.sendNEC(IR_SEND_DATA);
            ir_sent = true;
        }
    } else {
        ir_sent = false;
    }

    delay(100);
}

IR_SEND_DATA の 0x41B6659A が上記で解析した点灯するための信号で、 これを IRsend クラスの sendNEC メソッドで送信している。

タイマーの時刻は ボタンA(正面の「M5」ボタン)で時、 ボタンB(側面の小さいボタン)で分を変更できるようにした。

送信するリモコンの信号を変えればいろいろと応用できそう。