【DIY × IoT】電子工作でお風呂のセンサーを作ってみる♬ – センサー編

2020年3月5日DIY × IoT

お風呂を溜めている時に水量が一定まで溜まったらお知らせしてくれると嬉しいですよね!
お風呂の設備についていれば良いのですが、残念ながら私の家にはついていないので、作ってみることにしました。

この記事では、お風呂に置いて水位を測るセンサー部分を作っていこうと思います。

全体の要件についてはこちらの記事をご覧ください。

水位センサーの要件

スポンサーリンク

水位が【ここ】まできたら!の【ここ】に水分を検知するセンサーを垂らしてそこまで溜まったらBluetooth通信で通知します。

お風呂に配置するのでコンセントがないため、電池で駆動するようにプログラムしないといけないということになります。

材料を揃える

水分を検知するセンサー

水分を検知するセンサーにはこちらを使用しました。
土壌センサーなので、普通は家庭菜園だったり観葉植物を育てる際の土壌水分を測るのに利用するのが正しいのでしょうけど、水分が測れればよいので十分でしょう。

【仕様】
動作電圧:3.3-5.5 VDC 
出力電圧:0-3.0VDC
インタフェース:PH2.0-3P 
寸法:98mm x 23mm 

Bluetooth通信で通知するマイコン

当初「ESP32 Devkit V1」を使用する予定でしたが、電池での駆動ができないことがわかり、急遽電池での駆動が可能な「ESP32 DevkitC V4」を購入してこちらを使用するようにしました。

ESP32 DevkitC V4

配線する

スポンサーリンク

「ESP32 DevkitC V4」のPINOUTはこちらになります。

PINOUTに合わせて配線します。
最終的に配線は下記のようになりました。

ESP32水分センサー
GNDGND
5VVCC
GPIO36AUTO

電源は3.3Vから取るようにして完了です。

ESP32 DevkitC V4から伸びている黒、赤、黄の線は水分センサーの黒、赤、黄にそれぞれ繋ぐ

Arduinoスケッチ

センサーとマイコンを繋ぐ

まずは、センサーとマイコンを繋いで、水分を検知できるようにします。

GPIO36ピンから読み取ります。読み取った値はカラカラの状態で4095となっています。そこから水分を検知すると3900→3800とどんどん下がっていきます。

下記のプログラムは閾値(いろいろ実験した結果3395あたりがちょうどよかった)を下回ったら、水がセンサーのところまで来ている(水を検知した)として、GPIO25ピンに出力しています。
GPIO25ピンにLEDを繋ぐと光らせることができます。

int sensor_pin = 36;
int sensor_output;
int level;

void setup() {
   Serial.begin(115200);
   while (!Serial);
   delay(2000);
   }
void loop() {
   sensor_output= analogRead(sensor_pin);
   
   if (sensor_output < 3395) {
        level = 255;
   } else {
        level = 0;
   }

   dacWrite(25, level);
   Serial.print("sensor_output : ");
   Serial.println(sensor_output);
   delay(5000);
}

BLEを組み込む

センサーとマイコンを繋いで水分が検知できるようになったら、次はBLE(Bluetooth Low Energy)で信号を送り、水を検知したことを別のデバイスに通知する仕組みを組み込みます。

BLEには2つの通信形態があり、今回はセンサーで読み取った値を送るだけなのでブロードキャスト方式を使用します。
ブロードキャスターを作るにあたって、こちらのサイトを参考にさせていただきました。
とても細かく解説されていて、初心者の私でも理解できました。
http://marchan.e5.valueserver.jp/cabin/comp/jbox/arc212/doc21202.html

水分センサーから読み取った値をそのままBluetoothに乗せて送信しています。

#include <BLEDevice.h>
#include <BLEServer.h>
#include <esp_deep_sleep.h>
#include <DHT.h>                    // DHTセンサー用

/* 基本属性定義  */
#define DEVICE_NAME "ESP32"         // デバイス名
#define DEVICE_NUMBER 1             // デバイス識別番号(1~8)
#define SPI_SPEED   115200          // SPI通信速度
#define DHTTYPE     DHT11           // DHTセンサーの型式

RTC_DATA_ATTR static uint8_t seq_number;    // RTCメモリー上のシーケンス番号
const int sleeping_time = 10;       // ディープスリープ時間(秒)
const int advertising_time = 1;     // アドバータイジング時間(秒)

/* DHTセンサー*/
const int DHTPin = 14;              // DHTセンサーの接続ピン
DHT   dht(DHTPin, DHTTYPE);         // DHTクラスの生成

/* LEDピン */
const int ledPin = 2;              // LEDの接続ピン

/* プッシュボタン */
const int buttonPin = 32;           // プッシュボタンの接続ピン

bool bAbnormal;                     // デバイス異常判定

/*****************************************************************************
 *                          Predetermined Sequence                           *
 *****************************************************************************/
void setup() {
    doInitialize();                           // 初期化処理をして
    BLEDevice::init(DEVICE_NAME);             // BLEデバイスを初期化する

    int buttonState = digitalRead(buttonPin); // プッシュボタンが押されたら異常状態にする
    if (buttonState == HIGH) {
        bAbnormal = true;
        Serial.println("** Abnormal condition! **");
    }
    else {
        bAbnormal = false;
    }

    // BLEサーバーを作成してアドバタイズオブジェクトを取得する
    BLEServer *pServer = BLEDevice::createServer();
    BLEAdvertising *pAdvertising = pServer->getAdvertising();
    // 送信情報を設定してシーケンス番号をインクリメントする
    setAdvertisementData(pAdvertising);
    seq_number++;

    // 所定の時間だけアドバタイズする
    pAdvertising->start();
    Serial.println("Advertising started!");
    delay(advertising_time * 1000);
    pAdvertising->stop();

    if (bAbnormal) {                          // 異常状態なら
        pinMode(ledPin, OUTPUT);
        int ledState = HIGH;
        for (;;) {
            digitalWrite(ledPin, ledState);    // LEDを点滅させて処理を停止する
            ledState = !ledState;
            delay(500);
        }
    }
    // 外部ウェイクアップを設定してディープスリープに移行する
    esp_sleep_enable_ext0_wakeup(GPIO_NUM_32, 1);
    Serial.println("... in deep sleep!");
    esp_deep_sleep(sleeping_time * 1000000LL);
}

void loop() {
}

/*  初期化処理  */
void doInitialize() {
    Serial.begin(SPI_SPEED);
    pinMode(buttonPin, INPUT);        // GPIO設定:プッシュボタン
    dht.begin();                      // センサーの起動
}

/*****************************< Other functions >*****************************/
/*
  アドバタイズデータに送信情報を設定する 
*/
void setAdvertisementData(BLEAdvertising* pAdvertising) {
    // 水位を読み取る
    int sensor_pin = 36;
    uint16_t sensor_output;
    int ledState;

    sensor_output= analogRead(sensor_pin);

    if (sensor_output < 3395) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    pinMode(ledPin, OUTPUT);
    digitalWrite(ledPin, ledState);

    // string領域に送信情報を連結する
    std::string strData = "";
    strData += (char)0xff;                  // Manufacturer specific data
    strData += (char)0xff;                  // manufacturer ID low byte
    strData += (char)0xff;                  // manufacturer ID high byte
    strData += (char)DEVICE_NUMBER;         // サーバー識別番号
    strData += (char)bAbnormal;             // 異常状態判定
    strData += (char)seq_number;            // シーケンス番号
    strData += (char)(sensor_output & 0xff);          // 水位センサーの下位バイト
    strData += (char)((sensor_output >> 8) & 0xff);   // 水位センサーの下位バイト
    strData = (char)strData.length() + strData; // 先頭にLengthを設定

    // デバイス名とフラグをセットし、送信情報を組み込んでアドバタイズオブジェクトに設定する
    BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
    oAdvertisementData.setName(DEVICE_NAME);
    oAdvertisementData.setFlags(0x06);      // LE General Discoverable Mode | BR_EDR_NOT_SUPPORTED
    oAdvertisementData.addData(strData);
    pAdvertising->setAdvertisementData(oAdvertisementData);
}

最後に

スポンサーリンク

配線する際はいきなりハンダ付けしてしまうと間違えた場合に取り返しがつかなくなるので、ブレッドボードという道具を使いました。
最終的にはプリント基盤に配置して動線を繋いで、ハンダ付けをして完成です。

このままでは電池を入れたらひたすら水分を測り続けるので、スイッチをつけて使用しない時は電源をOFFにできるようにすると良いかもしれません。
スイッチをONにした場合にLEDを点灯するようにすると、電源の状態がわかるのでより良いかもです。