ESP32を使ってSwitchBot温度計とNature Remoを連携する話(第6回)

冷房/暖房を考慮したエアコン制御ロジックの再設計


はじめに

前回までで、以下の機能は一通り実装できていた。

  • SwitchBot 温度計からの温湿度取得
  • 温度閾値に基づくエアコン ON / OFF 制御
  • Web UI からの閾値設定

一通り動くようになったあと、 制御ロジックをあらためて眺めてみたところ、気になる点が出てきた。

エアコンの運転モード(冷房/暖房)をまったく意識していなかった

という点である。


既存制御ロジックの内容

修正前のエアコン制御ロジックは、次のような条件分岐だった。

if (currentTemperature >= temperatureThresholdHigh && !airConditionerState) {
  controlAirConditioner(true);
  airConditionerState = true;
} else if (currentTemperature <= temperatureThresholdLow && airConditionerState) {
  controlAirConditioner(false);
  airConditionerState = false;
}

このロジックは、

  • 温度が高くなったらエアコンを ON
  • 温度が下がったらエアコンを OFF

という動きをしている。

当初は「とりあえず動けばOK」と思っていたので、 この時点では特に違和感はなかった。


暗黙的前提条件の存在

ただ、よく考えてみると、 このロジックが自然に成立するのは 冷房の場合だけである。

つまり、

  • 暖房運転をしたい場合
  • 冷房と暖房を切り替えて使いたい場合

には、このままでは対応できない。

ロジックの中に、

「今は冷房なのか、暖房なのか」

という情報が存在していなかった。


問題構造の整理

ここで一度、前提を整理してみる。

エアコンの実際の仕様

  • 冷房と暖房がある
  • 冷房と暖房では、温度制御の考え方が逆になる

今のロジックで足りていない点

  • 冷房/暖房という状態を保持していない
  • 温度判定のルールが 1 パターンしかない

結果として、

冷房で使うことを前提にしたロジックになっていた

という状態だった。


要件の再整理

コードから一度離れて、 「やりたいこと」を書き出してみる。

やりたかったこと

  • 冷房と暖房を切り替えられるようにしたい
  • モードに応じて ON / OFF の判断を変えたい

温度判定の整理

運転モード ON 判定 OFF 判定
冷房 高温閾値を上回ったとき 低温閾値を下回ったとき
暖房 低温閾値を下回ったとき 高温閾値を上回ったとき

同じ「高/低閾値」でも、 意味が逆になることが分かる。


運転モード表現の必要性

ここまで整理してみて、 そもそも「今が冷房か暖房か」を プログラムが知らないと判断できない、ということに気づいた。

そこで、運転モードをどう表現するかを考えた。


真偽値による運転モード表現

bool isCooling;

考え方

  • true なら冷房
  • false なら暖房

という 2 択で表す方法である。

書き方の例

if (isCooling) {
  // 冷房時の制御
} else {
  // 暖房時の制御
}

使ってみて感じたこと

シンプルではあるが、

  • true / false の意味を毎回思い出す必要がある
  • あとからモードを増やしたくなったときに困りそう

という点が少し気になった。


列挙型による運転モード表現

enum class AcMode {
  Cooling,
  Heating
};

相違点

AcMode という型自体が、 「エアコンの運転モード」を表すようになる。

  • AcMode::Cooling
  • AcMode::Heating

と書けるので、意味がコード上にそのまま出てくる。

条件分岐の例

if (acMode == AcMode::Cooling) {
  // 冷房用制御
}

読み返したときに、 「何を判定しているのか」を考えなくて済む。


安全性について

enum class は、 意図しない値を代入できない。

acMode = 1;     // コンパイルエラー
acMode = true; // コンパイルエラー

うっかりしたミスを 早い段階で防げるのは助かる。


拡張を考えた場合

将来、

  • 送風
  • 除湿

を足したくなった場合も、 自然に拡張できそうだと感じた。

enum class AcMode {
  Cooling,
  Heating,
  Fan,
  Dry
};

本システムでの選択

今回のシステムでは、

  • 実際の機器を動かす
  • 間違った制御は困る
  • あとから機能を足す可能性が高い

という前提がある。

そのため、

現時点では enum class を使用する

とした。


修正後制御ロジックの構造

運転モードごとに、 判定ロジックを分ける形にした。

冷房モード時

if (currentTemperature >= temperatureThresholdHigh && !airConditionerState) {
  controlAirConditioner(true);
  airConditionerState = true;
} else if (currentTemperature <= temperatureThresholdLow && airConditionerState) {
  controlAirConditioner(false);
  airConditionerState = false;
}

暖房モード時

if (currentTemperature <= temperatureThresholdLow && !airConditionerState) {
  controlAirConditioner(true);
  airConditionerState = true;
} else if (currentTemperature >= temperatureThresholdHigh && airConditionerState) {
  controlAirConditioner(false);
  airConditionerState = false;
}

条件式は似ているが、 考え方はまったく逆になっている。