改造は自己責任で行ってください
#目的
ウォーターサーバーまでいらないけど、直ぐにお湯が出る環境が欲しくて、以下の購入し、(壊した)試作記録です。
##主要使用物
・ネスカフェ ドルチェ グスト - インフィニッシマ
https://nestle.jp/brand/ndg/product/infinissima.html
・ESP-WROOM-02開発ボード
http://akizukidenshi.com/catalog/g/gK-12236/
・BME280使用 温湿度・気圧センサモジュールキット
http://akizukidenshi.com/catalog/g/gK-09421/
###しでかした理由
・なぜこの機種を?
・水をほしい時に沸騰させてくれる
・ドルチェグストとして使うこともあるけど、基本お湯が欲しい
→ので、マニュアル方式
・なんで、改造?
・カップを置いて、ポチッと押したらお湯が出て欲しい!
→電源+レバー操作をしないとイケないのがチトいただけない(マニュアルを選んでおいて、、
・レバー操作の時間をマイコンで管理すれば、面白いのでは?(ここ本音)
なので、壊してみました!
(壊す前に基本動作確認をしなかったので、壊れていたのか、何で壊したのかが分かってないのが実態だったりします。もう保証は付かないので、2700円の授業料だと思ってます
#したこと と すること(予定)
###ハードウェア面
1.分解T-10(いじり防止)ドライバ購入して、裏側をパカッと
例:ベッセル(VESSEL) パワーグリップトルクスドライバー 5400TX T10HX80
https://www.amazon.co.jp/dp/B002P84538
(こういうネジの頭なので、、、
2.白いケースの中の基盤を取り出し。ケースの縁に固定具があるので削り取り
注:取り出すのに削り切ってしまうので、電源ボタンを強く押したら凹んで戻らない状態に、、、
(まぁ、ソフトウェア起動になるからいいんですが、、、、
・電源ボタン(検証済)
・湯煎レバー:(検証済)
・GND(検証済)
実際に通電実験と本体の起動に成功
・緑ランプTP13(検証済)
・赤ランプTP22(検証済)
通電検査用の半田ポイントがあるので、そこを利用
・5v?電源
実装機体で失敗したので、実装不明、、、、
3.5物理スイッチの操作検知
頭のスイッチが「赤」側にないとお湯が出ない仕様が発覚(バラすの超絶硬いし、複雑、、、
→なので、回路だけ「ON」になっているだけではお湯は出ない、、、
→以下の方法でハンドルの「状態」を取得
(GNDを共通化しているならフォトカプラではなくてトランジスタ使うべきなんでしょうが、使い方が未だに分かってないんです、、、
4. 配線
回路図を書くのは本業ではないので、、概略だけ、、、
###ソフトウェア面
・フロー
マイコン側のボリューム調整:湯煎ボタンの押し時間=湯量設定
↓
マイコン側のボタンを押す
↓
湯量が多ければ確認状態でもう一度ボタンを押す
↓
緑ランプ動作ONとハンドルが「赤」になるまで待機
↓
緑ランプ点灯後に、湯煎レバーをオン
・安全面としては
インターセプトとして、LEDの変化、ハンドル操作、スイッチが押されたら湯煎の動作を停止。
・機能面の追加としては
ESP32ESP8266で作ったならばWifi連携ですかねぇ~。操作権限はネットには渡さないでしょうけど。
400mlまで連続給湯できるなら、300mlは3分、400mlなら3分、5分のタイマーをセットさせたいかな
##プログラム成果物
#include //タイマー
#include //温度センサードライバ
#include //i2cを使うために
#include //Wifiを使うために
extern "C" { //E8266用アナログ値取得
#include "user_interface.h"
}
//Wifi設定
const char* ssid = -SSID-;
const char* password = -PASS-;
//ifttt側
const char* host = "maker.ifttt.com";
const int Port = 80;//ホントはSSLしたかった、、、
const char* iftttKey = -WEBHOOKKey-;
const char* iftttEvent1 = -温度系用-;
const char* iftttEvent2 = -飲み物履歴-;
//温度センサー
#define I2C_SCL 5 // GPIO5
#define I2C_SDA 4 // GPIO4
Adafruit_BME280 bme;
//スピーカー
#define SPEAKER 15 //GPIO15
//操作ボタン
#define STATEBT 0 //GPIO0
//ドルチェグスト:ステータス関係
#define LEDGR 2 //GPIO2
#define LEDRD 12 //GPIO12
#define HLD 16 //GPIO16
//操作関係
#define CTRPOWRE 14 //GPIO14
#define CTRWATER 13 //GPIO13
//コマンド管理
#define STOPED 0
#define CONFIRM 1
#define WAITING 2
#define WOKRING 3
volatile int state = STOPED; //コマンド状態管理
volatile int workt = 0; //湯煎残り時間管理
volatile unsigned long sttime = 0; //湯煎実行開始時刻管理
//タイマー
Ticker ticker1; //3分ラーメンタイマー
Ticker ticker2; //5分ラーメンタイマー
Ticker ticker3; //タイムアウト管理タイマー
Ticker ticker4; //気温データ取得タイマー
//タイマー実行用フラグ(通信・GPIO操作はメインでしないといけないので)
volatile int tic1F = false;
volatile int tic2F = false;
volatile int tic3F = false;
volatile int tic4F = false;
void setup() {
//各ピンを入出力設定
pinMode( STATEBT, INPUT_PULLUP );
pinMode( LEDGR, INPUT_PULLUP );
pinMode( LEDRD, INPUT_PULLUP );
pinMode( HLD, INPUT_PULLUP );
pinMode( CTRPOWRE, OUTPUT);
pinMode( CTRWATER, OUTPUT);
//出力を一応初期化
digitalWrite(CTRPOWRE, LOW);
digitalWrite(CTRWATER, LOW);
//デバック用
Serial.begin(115200);
//温度計i2cを初期化:センサーが見つからなければプログラムを停止
Wire.begin(I2C_SDA, I2C_SCL);
if (!bme.begin(0x76)) { // SDO GND(Open):0x76 VDD:0x77
Serial.println("Could not find a valid BME280 sensor, check wiring!");
while (1); // Hung up!
}
//初回時の気温を取得
ReadWeather();
//電源起動なので10分ごとに温度測定
ticker4.attach_ms( 10 * 60 * 1000, ChangeFlag4);
}
//BME280からデータ取得
void ReadWeather() {
//センサデータ取得
float Temp = bme.readTemperature();
float Pres = bme.readPressure();
float Humd = bme.readHumidity();
//データ異常ならば再取得:センサーの測定範囲外は再取得
while(isnan(Temp) or isnan(Pres) or isnan(Humd) or Temp < -40 or Temp > 86 or Pres < 30000 or Pres > 110100){
delay(500);
Temp = bme.readTemperature();
Pres = bme.readPressure();
Humd = bme.readHumidity();
Serial.println("re-Mesuering!");
}
//送信データの整形
String Data = String("{ \"value1\" : \"") + String( Temp, 2) + "\", "
+ "\"value2\" : \"" + String( Humd, 2) + "\", "
+ "\"value3\" : \"" + String( Pres / 100.0F, 2) + "\" } ";
Serial.println("Temp:" + String(Temp) + " Hum:" + String(Humd) + " Pres:" + String(Pres));
//Wifiを使って送信
SendMessage( iftttEvent1, Data);
}
//三分タイマー:お遊び
void chikin() {
int octer = 150;
tone(SPEAKER, 330, octer) ; //ミ
delay(octer);
tone(SPEAKER, 277, octer) ; //ド#
delay(octer);
tone(SPEAKER, 330, octer * 2) ; //ミ
delay(octer * 2);
tone(SPEAKER, 370, octer * 2) ; //ファ#
delay(octer * 4);
tone(SPEAKER, 370, octer) ; //ファ#
delay(octer);
tone(SPEAKER, 440, octer) ; //ラ
delay(octer);
tone(SPEAKER, 370, octer) ; //ファ#
delay(octer);
tone(SPEAKER, 277, octer * 2) ; //ド#
delay(octer * 2);
tone(SPEAKER, 330, octer * 2) ; //ミ
delay(octer * 2);
}
//5分タイマー:お遊び
void charamela() {
int octer = 150;
tone(SPEAKER, 440, octer) ; //ラ
delay(octer);
tone(SPEAKER, 494, octer) ; //シ
delay(octer);
tone(SPEAKER, 554, octer * 4) ; //ド#
delay(octer * 4);
tone(SPEAKER, 494, octer) ; //シ
delay(octer);
tone(SPEAKER, 440, octer) ; //ラ
delay(octer * 5);
tone(SPEAKER, 440, octer) ; //ラ
delay(octer);
tone(SPEAKER, 494, octer) ; //シ
delay(octer);
tone(SPEAKER, 554, octer) ; //ド#
delay(octer);
tone(SPEAKER, 494, octer) ; //シ
delay(octer);
tone(SPEAKER, 440, octer) ; //ラ
delay(octer);
tone(SPEAKER, 494, octer * 4) ; //シ
delay(octer * 4);
}
//緊急用サウンド
void AlertSound() {
int octer = 150;
tone(SPEAKER, 523, octer * 2) ; //ド
delay(octer * 3);
tone(SPEAKER, 523, octer * 2) ; //ド
delay(octer * 2);
}
//注意用サウンド
void NoticeSound() {
int octer = 150;
tone(SPEAKER, 523, octer) ; //ド
delay(octer * 2);
tone(SPEAKER, 523, octer) ; //ド
delay(octer * 2);
}
//確認用サウンド
void OKSound() {
int octer = 200;
tone(SPEAKER, 523, octer) ; //ド
delay(octer);
}
//データ送信
void SendMessage(const char* Event, String &Data) {
//Wifi接続開始
WiFi.begin(ssid, password);
//接続するまで待機
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
Serial.println("WiFi connected");
//Webクライアントで接続できるかをチェック
WiFiClient client;
if (!client.connect(host, Port)) {
Serial.println("connection failed");
return;
}
//接続先URLを作成
String url = "/trigger/";
url = url + Event + "/with/key/" + iftttKey;
Serial.println(Data);
//HTTPで送信
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"User-Agent: BuildFailureDetectorESP8266\r\n" +
"Connection: close\r\n" +
"Content-Type: application/json; charset=utf-8\r\n" +
"Content-Length: " + Data.length() + "\r\n\r\n" +
Data +
"\r\n"
);
//送信確認
Serial.println("request sent");
while (client.connected()) {
String line = client.readStringUntil('\n');
if (line == "\r") {
Serial.println("headers received");
break;
}
}
Serial.println("closing connection");
}
//タイマー実行
void ChangeFlag1() {
tic1F = true;
}
void ChangeFlag2() {
tic2F = true;
}
void ChangeFlag3() {
tic3F = true;
}
void ChangeFlag4() {
tic4F = true;
}
void loop() {
//各入力の状態を取得
int BT = digitalRead(STATEBT);
int GR = digitalRead(LEDGR);
int RD = digitalRead(LEDRD);
int HD = digitalRead(HLD);
int AD = system_adc_read();
Serial.print(" BT:");
Serial.print(BT);
Serial.print(" GR:");
Serial.print(GR);
Serial.print(" RD:");
Serial.print(RD);
Serial.print(" HD:");
Serial.println(HD);
Serial.print("AD:");
Serial.print(AD);
Serial.print(" workt:");
Serial.print(AD * 0.053);
Serial.print(" ml:");![download3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/602722/cd5aec87-21cd-dab7-096b-8f9d0d628b6e.gif)
Serial.println(AD * 0.053 * 7.3);
/*
Serial.print("Flag1:");
Serial.print(tic1F);
Serial.print(" Flag2:");
Serial.print(tic2F);
Serial.print(" Flag3:");
Serial.print(tic3F);
Serial.print(" Flag4:");
Serial.println(tic4F);
*/
//Confirm状態とWaiting状態が30秒続く場合、STOPEDに戻る
if (tic3F) {
tic3F = false;
NoticeSound();
state = 0;
workt = 0;
sttime = 0;
ticker3.detach();
}
switch (state) {
case STOPED:
if (BT == 0) {
//ドルチェグストが起動していなければ電源On
if (GR == 1 and RD == 1) {
Serial.println(">>PowerOn!");
digitalWrite(CTRPOWRE, HIGH);
delay(100);
digitalWrite(CTRPOWRE, LOW);
}
//300ml以上だと確認Confirmへ
if (AD > 775) {
NoticeSound();
ticker3.once_ms(30 * 1000, ChangeFlag3); //CONFIRM用タイムアウトを設定
state = state + 1;
} else {
//300ml未満だと待機状態Waitingへ
OKSound();
state = state + 2;
}
}else{
//停止状態の時のみサウンドと送信を実行
if (tic1F) {
tic1F = false;
chikin();
}
if (tic2F) {
tic2F = false;
charamela();
}
if (tic4F) {
tic4F = false;
ReadWeather();
}
}
Serial.print("state:STOPED");
break;
case CONFIRM:
Serial.print("state:CONFIRM");
if (BT == 0) {
OKSound();
Serial.println(">>Confirm:OK!");
ticker3.detach(); //CONFIRM用タイムアウト設定を解除
ticker3.once_ms(30 * 1000, ChangeFlag3); //WAITING用タイムアウトを設定
state = state + 1;
}
break;
case WAITING:
Serial.print("state:WAITING");
//ヒーターの準備OKとハンドルが「赤」になっていれば湯煎実行
if (GR == 0 and HD == 0) {
OKSound();
Serial.println(">>Machine:Ready!");
ticker3.detach(); //WAITING用タイムアウト設定を解除
workt = AD * 0.053; //0.053sec/bitは実際に湯量と時間の相関を確認した結果より
sttime = millis(); //湯煎開始時刻を記録
digitalWrite(CTRWATER, HIGH); //湯煎開始
state = state + 1;
}
break;
case WOKRING:
int StopRea = 0; //送信用データの理由を管理
Serial.print("state:WORKING");
if (GR == 1 or RD == 0 or BT == 0 or HD == 1) { //ハンドル・ボタン・LEDの変更があれば緊急停止
Serial.println(">>EMERGENCY STOP!!");
digitalWrite(CTRWATER, LOW); //湯煎を停止
AlertSound(); //警告音
workt = 0; //湯煎カウントを0に
StopRea = 1; //停止理由を緊急(手動)に設定
} else { //何事も無ければカウントダウン
Serial.print(">>count:");
Serial.println(workt);
workt = workt - 1;
}
//湯煎カウントが0になれば停止
if (workt < 1) {
digitalWrite(CTRWATER, LOW); //湯煎を停止
int workS = (millis() - sttime) / 1000; //稼働時間を計算
Serial.print(">>Worked!!:");
Serial.print( workS );
Serial.println("second(s)");
//40秒(300ml)以上であれば、3分タイマー
if (workS > 40) {
ticker1.once_ms(180 * 1000, ChangeFlag1);
}
//50秒(350ml)以上であれば、5分タイマー
if (workS > 50) {
ticker2.once_ms(300 * 1000, ChangeFlag2);
}
//湯煎履歴を送信
String Data = String("{ \"value1\" : \"") + workS + "\", "
+ "\"value2\" : \"" + StopRea + "\" } ";
SendMessage( iftttEvent2, Data);
//完了音
OKSound();
state = 0;
}
break;
}
delay(1000);
}
##参考文献
分圧回路:
ちょと裏技っぽいけど、ESP8266 でAD変換やる方法
https://qiita.com/azusa9/items/26e74e4e0d5773ce9c41
こう繋いだら動くと書いていただいたのですが、回路上でどの順番になっていくのがわからなくなって何度も試作回路を作る羽目に、、、、
圧電スピーカー関係:
Arduinoで童謡「鯉のぼり」を流してみよう
https://mag.switch-science.com/2015/04/29/gwprojact_koinobori/
音程の数値を参照。
それそれのメロディは個人の趣味なので、、、、、
温度計関係:BME280
aitendoで売っているボッシュの温湿度・気圧センサBME280と温度・気圧センサBMP280を使ってみる
https://intellectualcuriosity.hatenablog.com/entry/2018/04/09/221041
ESP8266を使って温度センサ情報をIFTTTに投げる
https://qiita.com/triwave33/items/76c5ba9f18631b96785d
温度センサーの値が飛んでいったり、値が高かったり、nanが出たりと書き方以外のところで半田で壊したのかとアタフタ。
飛んでいったのはESP8266のi2c通信に抵抗が入っていない?と理解したのでJ1,J2,J3の全てショートさせることと、風が強いとダメっぽいようでちゃんと箱に入れたら動いた。
箱に入れた事によって、温度がスゴく高くなるので発熱源から離すようにしないといけないのか、、、、
IFTTTの使い方:
ESP8266 で IFTTT を使ってtwitterにつぶやく
https://n.mtng.org/ele/arduino/ifttt/index36.html
FTTT, Beebotte, Mosquitto, SSL/TLS を使って, なるべく安全に Google Home から Raspberry Pi や ESP8266 を操作する
https://qiita.com/Guwashi/items/e1ea9c61af4715198edc
5-1.人を検知してリモコン制御とメール送信 (IFTTT)
https://kit.socinno.com/5_1_e/
結局ESP8266でSSL通信の仕方が分からなくて、HTTP通信で妥協。何が悪いんだろうか、、、、、
編集中記(2020/05/03)
1台目を絶賛壊して、2台めを発注。1台目で予定していることを試して、2台目は動作確認した上で実装(それでも壊したら、うん、この企画は諦めます、、、
一応、スイッチ、レバー、5v、ステータスは見れるところまで引き出しは出来てるんですが、なんでかポンプの駆動音がしないというwなんでじゃろ?(一応、ポンプもヒーターにも通電しているというのに、、、
まぁ、誰かの興味を引けたなら、この企画も意味があったということでw
(誰か、チップ名の分からないマイコンの電源ピンの見つけ方知ってたら教えてください
編集後記(2020/05/25)
無事に水とカップを置いたらお湯が貰える環境を実装完了。作業時間は60時間くらい?
ソフトウェア側を考えるのは慣れているから、まだ頭は回ったけど、ハードウェア側:電子回路を考えるのは本当に疲れた、、、。壊したら、コストがかかるし、半田は怖いし、、、、、
やはり、ハードな話をソフト側に引っ張ってくるのがIoTの課題だと実感。
ソフト側に引っ張ってきたら色々できるから、誰かのネタになればいいな。
企業ならば、社員証をかざして休憩の回数把握とか、ドルチェグストのカプセルの利用確認とかかな?
大学の研究室ならば、配属された3年生位にコレと同じことをさせて、IoTとして実装するプロセス体験と新しい連携の発想の起点にさせるかな?
小学生・中学生のプログラムの授業で動くものを見せるのもありかなっと思ったり。Sketchとかで連携できるように本体にAPIソケットを着けてくれたら男の子は食いつきそう?(ネ◯レの人には将来のコーヒーを飲む人間を増やすための教育教材として開発・提供してくれたらいいなぁ、、、
作業曲:sm33705496
テンションを上げていれば何とかなるものですね。