流し忘れても安心!立ったら勝手に流してくれるリモコン
elchika初投稿です!宜しくお願い致します
みなさんは、トイレでうっかり流し忘れてしまうことありませんか?
高級なトイレの場合は、デフォルトで自動洗浄機能が付いていますが、安かったり、古いトイレの場合、自動洗浄機能はありません。
そこで思い立ち、「立ったら勝手に流してくれるリモコン」(早い話が自動洗浄機能)を製作しましたので、紹介しましょう。
まずは下ごしらえ
このリモコンを作るには、トイレを自動で流す機能が必要です。一から作るとなると、結構な労力です。そこで、出来合いの物を購入することにしました。
LIXIL(旧TOTO)から発売されている「流せるもん CWA-67B」という物を購入。コイツは、赤外線リモコンを使用して、トイレをモーター制御で流すというもの。
13000円位と少々お高めではありますが、モーターや赤外線リモコン、その他付属品が付いており、TOTO製の「流せるもん CWA-67B」の適合しているトイレではなかったのですが、付属品を微調整して、中のフロートを上げ下げできるようにすることで動かすことができました。
今まで通り、普通に流すこともできますが、リモコン操作でも流せることができ、何かちょっと感動。
でも、これはあくまでもスタートです。
「流せるもん~」の詳細は、私のブログで紹介しておりますので、ご参照ください。
リモコンを作る
当初は、折角リモコンがあるので、それを改造すればいいじゃないか…そう思い、センサーやマイコンを付けて改造したのですが、意外とセンサーが大飯食らいなので、電池2本では何日も使えないような代物になってしまいました(「流せるもん」は、単三電池2本)。
困っていた所、おあつらえ向きなガジェットを見つけました。elchikaをご覧になっている方ならお馴染みの「M5Stick C」です。
私はどちらかというと、部品を組み合わせてはんだ付けするのが好きなのですが、色々調べると、M5Stick CにジャストフィットするHATやM5Stack・Grove用に用意されているモジュール類がそのまま使えるらしく、私の用途では、HATとM5STACK用IRモジュールを使うと用が足りてしまいます。
後はプログラムさえ作ればよく、ホント便利な世の中になったものです(すっかりオヂさんな気分)。
昔のように、回路を組んでガシガシやるのではなく、あるものを使って、スマートに使いこなすのが、今の時代にマッチした電子工作の姿なのかも知れませんね。
オリジナルのリモコンと自動洗浄用のリモコンの2個あることになりますが、オリジナルの方は常時動いているわけではないし、2個あることによる支障はないので、まっ、結果オーライです
これにも、実は顛末記があり、M5Stick Cに内蔵されているIR LEDでも狭いトイレの中では、充分動いてくれたのですが、ToFユニットを付けてしまうと、IR LEDの穴が塞がってしまうというオチが。
折角ToFユニットを買ったので、それを生かすことにし、IRの方を別の物(M5Stack用赤外線送受信ユニット)とすることにしたのですが、今度は、何故か赤外線の飛びが悪く、結局は、M5Stick C+ToF測距センサユニット(https://www.switch-science.com/catalog/5219/)の組み合わせに変更しました。
人の動きをとらえるセンサーには人感センサーがよく使われますが、センサー付きLED電球で経験された方はご存じと思いますが、明るい所では全く役に立ちません。
赤外線を使って、戻ってくる時間を測定するというToF(タイムオブフライト)センサーは(使われているデバイスは)940nmの赤外線を使用しており、明るさに影響を受けません。
今回の用途には、まさに打ってつけです!
材料
部品 | 個数 | 値段 | 購入先 |
---|---|---|---|
M5Stick C | 1 | ¥1,980 | スイッチサイエンスなど |
M5Stack用ToF測距センサユニット | 1 | ¥1,177 | スイッチサイエンスなど |
(以下は、お好みに応じて)
部品 | 個数 |
---|---|
USB Cケーブル | 1 |
USB 5V電源 | 1 |
送料を除けば、ケーブル込みで大体4,000円少々でできました。
M5Stick Cには、バッテリーが内蔵されているのですが、バッテリー切れを嫌って、私はUSB電源を使用しました。
M5Stick-Cの電源をOFFにしていても、ある程度電流が流れているのか、一晩放置していたら、全く動かなくなってしまいました。
慌てて、USB電源をつないでもダメでした。どうもプログラムが消えてしまったようで…。ユニットを付けるとダメなんでしょうか。トホホ…
そのような訳で、長時間使う向きにはUSB電源をお勧めします。
プログラム紹介
下記のプログラムは、私のオリジナルではなく、今回の用途のためにサンプルを合体させ、更にモディファイしたものです。
ToFサンプル:https://github.com/m5stack/M5-ProductExampleCodes/blob/master/Hat/tof-hat/Arduino/ToF
IRサンプル:https://github.com/crankyoldgit/IRremoteESP8266/issues/706
特に、IRの箇所は、TVやエアコンなどの実例はあれど、トイレ用などのものは殆ど見つけることができませんでした。
色々実験した所、上記のサンプルの結果が良かったので、IR送信の部分はそちらを参考にしました。
ライブラリはそのまま流用しているので、Arduino IDEのライブラリマネージャから必ずIRremoteESP8266をゲットしてくださいね。
それと、M5***を始めて使う人は、https://github.com/m5stack/M5StickCからライブラリ(Zipファイル)をゲットして、こちらもインストールしてください。
公式では、Arduino IDEのライブラリマネージャからM5StickCライブラリをインストールするような指示になっていますが、ファイルが足りなくて、悲しいかなコンパイルができません(2021年2月現在)。
早く改善して~
立ったら勝手に流してくれるリモコンの例(ArduinoIDE用)
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <M5StickC.h>
#include <Wire.h>
// byte SEND_PIN = 32; // IR Unit:32
byte SEND_PIN = 9; // 本体IR:9
IRsend irsend(SEND_PIN);
#define VL53L0X_REG_IDENTIFICATION_MODEL_ID 0xc0
#define VL53L0X_REG_IDENTIFICATION_REVISION_ID 0xc2
#define VL53L0X_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD 0x50
#define VL53L0X_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD 0x70
#define VL53L0X_REG_SYSRANGE_START 0x00
#define VL53L0X_REG_RESULT_INTERRUPT_STATUS 0x13
#define VL53L0X_REG_RESULT_RANGE_STATUS 0x14
#define ToF_ADDR 0x29//the iic address of tof
//「流せるもん」のコードに合わせています。
const uint16_t kInaxHdrMark = 9000;
const uint16_t kInaxHdrSpace = 4434;
const uint16_t kInaxBitMark = 600;
const uint32_t kInaxOneSpace = 1675;
const uint32_t kInaxZeroSpace = 570;
const uint64_t InaxData = 0x5C30CF; //「大」ボタンのコード
byte gbuf[16];
uint16_t dist;
uint16_t tmp1;
uint8_t i, countdown;
void measure_distance();
uint16_t bswap(byte);
uint16_t makeuint16(int, int);
uint16_t VL53L0X_decode_vcsel_period(short);
void write_byte_data(byte);
void write_byte_data_at(byte, byte);
void write_word_data_at(byte, uint16_t);
byte read_byte_data();
byte read_byte_data_at(byte);
uint16_t read_word_data_at(byte);
void read_block_data_at(byte, int);
void setup() {
// Wire.begin(0, 26, 400000);// join i2c bus (HATの場合)
Wire.begin(32, 33, 400000);// join i2c bus (ToFユニットの場合)
M5.begin();
M5.Lcd.setRotation(2);
irsend.begin();
pinMode(10, OUTPUT);
digitalWrite(10, HIGH);
}
void sendWashletCommand(uint64_t data, uint16_t nbytes, uint16_t repeat) {
irsend.enableIROut(38);
for (uint16_t r = 0; r <= repeat; r++) {
// Header
irsend.mark(kInaxHdrMark);
irsend.space(kInaxHdrSpace);
// Data
irsend.sendData(kInaxBitMark, kInaxOneSpace, kInaxBitMark, kInaxZeroSpace, data, nbytes, true);
// Footer
irsend.mark(kInaxBitMark);
irsend.space(100000);
}
}
void loop() {
tmp1 = 0;
measure_distance();
delay(100);
if (dist > 200){
digitalWrite(10,HIGH);
} else if (dist == 20){
//時々、誤動作なのか、一瞬 数値が20(0cmの距離)となる時があった。
//distが20になっても何もしないようダミーとする。
}else {
digitalWrite(10,LOW);
while (dist < 200){
//着座している時はループ
measure_distance();
delay(100);
}
//立ったら、30秒(30秒*2=60)ディレイ
//iを増減すると、離れてから流す時間を変えられます。
//変数countdownは、表示に用います。
countdown = 30;
i = 60;
while (i > 0){
//0.5秒間隔で、M5Stick CのLED(G10)を点滅させる。
if (digitalRead(10)){
digitalWrite(10, LOW);
M5.Lcd.setCursor(0, 80);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextFont(7);
M5.Lcd.print(countdown);
countdown--;
} else {
digitalWrite(10, HIGH);
}
i--;
delay(500);
}
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextFont(1);
M5.Lcd.setCursor(20,80);
M5.Lcd.print("Flash!");
digitalWrite(10, LOW);
sendWashletCommand(InaxData, 24, 0);
delay(2000);
M5.Lcd.fillScreen(BLACK);
digitalWrite(10, HIGH);
}
}
void measure_distance() {
write_byte_data_at(VL53L0X_REG_SYSRANGE_START, 0x01);
read_block_data_at(VL53L0X_REG_RESULT_RANGE_STATUS, 12);//read 12 bytes once
dist = makeuint16(gbuf[11], gbuf[10]);//split distance data to "dist"
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(20, 80);
M5.Lcd.setRotation(2);
M5.Lcd.setTextFont(1);
M5.Lcd.print(dist);
}
uint16_t bswap(byte b[]) {
// Big Endian unsigned short to little endian unsigned short
uint16_t val = ((b[0] << 8) & b[1]);
return val;
}
uint16_t makeuint16(int lsb, int msb) {
return ((msb & 0xFF) << 8) | (lsb & 0xFF);
}
uint16_t VL53L0X_decode_vcsel_period(short vcsel_period_reg) {
// Converts the encoded VCSEL period register value into the real
// period in PLL clocks
uint16_t vcsel_period_pclks = (vcsel_period_reg + 1) << 1;
return vcsel_period_pclks;
}
/*
* IIC Functions
*/
/* function description: write one byte data */
void write_byte_data(byte data) {
Wire.beginTransmission(ToF_ADDR);
Wire.write(data);
Wire.endTransmission();
}
/* function description: write one byte data to specifical register */
void write_byte_data_at(byte reg, byte data) {
Wire.beginTransmission(ToF_ADDR);
Wire.write(reg);
Wire.write(data);
Wire.endTransmission();
}
/* function description: read two bytes data to specifical register */
void write_word_data_at(byte reg, uint16_t data) {
byte b0 = (data &0xFF);
byte b1 = ((data >> 8) && 0xFF);
Wire.beginTransmission(ToF_ADDR);
Wire.write(reg);
Wire.write(b0);
Wire.write(b1);
Wire.endTransmission();
}
/* function description: read one byte data */
byte read_byte_data() {
Wire.requestFrom(ToF_ADDR, 1);
while (Wire.available() < 1) delay(1);
byte b = Wire.read();
return b;
}
/* function description: read one byte data from specifical register */
byte read_byte_data_at(byte reg) {
//write_byte_data((byte)0x00);
write_byte_data(reg);
Wire.requestFrom(ToF_ADDR, 1);
while (Wire.available() < 1) delay(1);
byte b = Wire.read();
return b;
}
/* function description: read two bytes data from specifical register */
uint16_t read_word_data_at(byte reg) {
write_byte_data(reg);
Wire.requestFrom(ToF_ADDR, 2);
while (Wire.available() < 2) delay(1);
gbuf[0] = Wire.read();
gbuf[1] = Wire.read();
return bswap(gbuf);
}
/* function description: read multiple bytes data from specifical register */
void read_block_data_at(byte reg, int sz) {
int i = 0;
write_byte_data(reg);
Wire.requestFrom(ToF_ADDR, sz);
for (i=0; i<sz; i++) {
while (Wire.available() < 1) delay(1);
gbuf[i] = Wire.read();
}
}
動作を解説
主要動作であるメインループの箇所をチャートで纏めてみました(ライブラリに関しては、各ソースをご参照ください)。
以下、簡単に説明します。
- ToFモジュールの測定値を確認し、値が200より大きければ(離れている)M5Stick Cに内蔵されている本体LEDを消灯。
- 200未満(着座)であれば、本体LEDを点灯。
- 再び測定値が200より大きくなれば(立つ)、30秒本体LEDを点滅させ、「大」のコードを赤外線送信する。
- LEDを消灯した後、距離を測るループに入る。
以上のような流れです。
実際に使ってみた
短いですが、YouTubeにアップしましたので、様子をご覧ください。
これは、我が家の介護用として製作したもので、いざ使ってみると、立ったときに自動で流してくれるのは便利よね…と思ったりして。立ってから30秒は短いかも知れません。
センサーに手をかざしてもOKですので、手を触れることなく自動洗浄できますよ。コロナ禍の今にピッタリかも。
「kInax~(赤外線のパルス幅を設定)」という変数をいじれば、他の会社のリモコンにも応用できるはずなので、興味ある方は、色々実験してみてくださいね
My weblog:https://kinopy.info/kinopy
投稿者の人気記事
-
kipopytokyo
さんが
2021/02/20
に
編集
をしました。
(メッセージ: 初版)
-
kipopytokyo
さんが
2021/02/28
に
編集
をしました。
-
kipopytokyo
さんが
2021/04/14
に
編集
をしました。
(メッセージ: 動画を追加しました。)
-
kipopytokyo
さんが
2021/12/08
に
編集
をしました。
-
kipopytokyo
さんが
2023/07/25
に
編集
をしました。
ログインしてコメントを投稿する