RSS блога
Подписка
Подключаем электропечь к андроиду с помощью Wemos D1 mini
- Цена: 2$
- Перейти в магазин
Давненько я не ковырял эту суперштуку под названием Wemos D1 mini, а тут оказия.
В общем есть электропечь Indesit:
Печь отличная, но в последнее время у нее стали баловаться регуляторы мощности. На двух конфорках работать стали нестабильно и непредсказуемо, невозможно выставить нужную температуру, то выкипает, то совсем гаснет.
Регуляторы мощности там стоят вот такие:
Я пробовал конечно их разбирать, чистил контакты, регулировал, но видимо металл уже изменил своих свойства и толку от этого практически не было.
И тут возникла мысль: «У меня есть Wemos D1 mini, в шаговой доступности есть твердотельные реле, почему нет?»
Итак беру пару вот таких твердотельных реле SSR-10DA:
Они популярные, есть на каждом углу. Есть несколько моделей, в маркировке нас интересуют цифра 10 (ток в амперах) и буквы DA (наша нагрузка будет работать на переменном напряжении, а управлять ею мы будем постоянным напряжением).
Напряжения Wemos D1 mini 3.3V вполне хватает, чтобы закрывать и открывать реле.
Итак, подключаем реле в разрыв питающего конфорку провода (между конфоркой и родным регулятором мощности конфорки). Сделал так на всякий случай, для дополнительной защиты. Т.е. чтобы наш Wemos D1 mini управлял конфоркой, нам нужно вручную ее включить (поставить на макс огонь). На управляющие контакты кидаем провода от пинов Wemos D1 mini (не забываем про полярность), осталось только запилить скетч.
Пока подпаивал пины, вспомнил, что у меня валяются без дела несколько датчиков, остались от старых проектов. И снова «почему нет?».
Подключил к Wemos D1 mini датчик атмосферного давления BMP180 (на борту он кстати имеет и датчик температуры), и датчик температуры и влажности DHT-21. Вот такие:
Подключение описывать не буду, в интернете море информации. Они очень популярные.
Пилим скетч:
Пара слов о скетче.
1. Наш скетч периодически опрашивает датчики. Показания каждого датчика заносятся в массив на 10 значений, после чего вычисляется среднее арифметическое (для большей точности можно вычислять медиану). Соотвественно эти данные о текущих показаниям могут быть в любой момент получены от него.
2. Показания датчиков один раз в час (период можно регулировать) заносятся в массив из 100 значений. И в любой момент этот массив по запросу может быть выдан. Это история показаний датчиков. 100 значений по часу, чуть более 4 суток получается.
3. Алгоритм работы собственно конфорок. Вычисляется время включения и выключения с учетом установленной мощности, установленного цикла. К примеру, длительноть рабочего цикла у нас установлена 10 секунд, а мощность 10%. Следовательно конфорка включится на 1 секунду, затем выключится на 9 секунд и далее по кругу.
4. Запоминаются время включения (для последующего отображения времени готовки) и также устанавливается таймер с обратным отсчетом, о нем позже.
5. Все эти данные могут быть получены с помощью GET-запроса. Ну например, наберем в адресной строке браузера домашнего компьютера следующий адрес:
192.168.0.100/set?info, в ответ получим строку в CSV-формате следующего вида
0|0|0|0|0|0|0|0|3600000|5.00
Числа, разделенные вертикальной чертой, каждое из которых отображает данные о текущем режиме (мощность первой конфорки, время ее работы, таймер, то же для второй, период истории, установленный цикл).
192.168.0.100 — это локальный адрес нашего Wemos D1 mini, он назначается роутером и узнать его можно при подключении Wemos D1 mini к компьютеру в мониторе порта.
192.168.0.100/set?klimat, нам вернет 751.26|21.69|49.16|23.68 — показания датчиков.
192.168.0.100/set?history нам вернет строку длиной почти в 4 тыс. символов примерно в таком виде:
746.28|23.69|54.19|26.14|133351518@746.21|25.09|52.78|25.34|132751517@…
Это показания датчиков с отметкой времени каждого (в ms назад от текущего момента).
Управление будет происходить запросами в виде
192.168.0.100/set?power_1=50 — этот запрос установит мощность первой конфорки на 50%.
Остальные запросы можно посмотреть в скетче.
Вопросы по скетчу можете задавать в комментариях или в личке, отвечу. Зачем там строка длиной в 4500 нулей? Это старая длинная история поиска путей стабильности работы Wemos. К слову, успешная. Wemos немножко неправильно работает с памятью при наращивании строк, и вот эти нули решают эту проблему.
Итак, уже все работает, можем получать информацию и отдавать команды. Осталось только выбрать UI-среду, которая будет делать это чуть более элегантно, чем строчки в браузере. Ну тут у кого к чему душа лежит. Можно сделать локальную веб-страничку с Ajax-запросами. Можно накодить простенькую windows-прогу в WPF. Но разумеется удобнее всего будет смартфон. Я выбрал конечно последнее, тем более, что немножко поверхностно могу кодить на Jave для Android. Кто не может, можете воспользоваться потрясной штукой MIT App Inventor 2. Эта среда позволяет писать проги для Андроида прям на коленке, без знания языка, просто на алгоритмах. В своем прошлом обзоре я пользовался именно ею и подробно о ней рассказывал. Причем результат визуально будет мало отличаться от «взрослой» проги, написанной в Android Studio.
Итак, написанная за пару вечеров программка выглядит так:
На главном экране вверху находятся кнопки выбора конфорки. Поскольку реле подключены к дальней левой и правой ближней, кнопки расположены именно в таком порядке. Кроме этого они служат индикатором текущего режима (меняют цвет от зеленого к красному в зависимости от установленной мощности, белые — если выключены). Для каждой конфорки все параметры (мощность, время, таймер) отдельные.
Ниже время, прошедшее с момента включения конфорки. Удобно сразу видеть, сколько готовится блюдо, без запоминания когда включил и т.д.
Еще ниже таймер. Выполняет 4 функции:
— во-первых, устанавливается ползунком,
— во-вторых, ведет обратный отсчет, показывая сколько осталось,
— в-третьих, выключает конфорку по истечении времени,
— в-четвертых, оповещает хозяина звуковым сигналом и вибрацией по окончании отсчета.
Ниже ползунок для установки мощности в процентах. Еще ниже кнопки для быстрой установки стандартных значений. К примеру, после пары недель использования мы поняли, что суп приятно кипит на 7% мощности, а мясо имеет идеальную золотистую корочку при 53% жарки, можем назначить на кнопки эти предустановленные значения в настройках:
Там же можно регулировать остальные параметры (рабочий цикл, частоту сохранения истории, IP адрес).
В принципе по печке это и все.
В верхнем эпп-баре есть еще пара кнопок (кроме кнопки настройки), средняя ведет нас к этому экрану (текущие показания датчиков):
Температуры две (цифровых), т.к. у нас два датчика, которые ее отображают.
А левая кнопка ведет нас в историю показаний:
Вот график с давлением действительно полезная штука. Тенденция его изменения очень хорошо кореллируется с самочувстсвием.
Пара слов об опыте использования. Супер, что сказать, даже лучше, чем мне казалось вначале. Во-первых, возможность выставить очень точно силу огня, которая будет поддерживаться идеально. Обычные регуляторы так не могут. У них меньше градаций, плюс каждый имеет свой разброс. Угадать не просто. Для блюд, которые хорошо выкипают, это важно.
Во-вторых, время и таймеры. Честно, лично я ленюсь каждый раз засекать время готовки, приходится делать на глаз, пробуя. Плюс частенько поставил, зачитался интересным обзором на Mysku и забыл. А таймер всегда напомнит и выключит огонь. Плюс иногда бывает нужно готовить блюдо несколько часов. Можно поставить и отлучиться в магазин, не переживая, что задержишься.
Дискомфорта от доставания телефона для включения конфорки нет, быстро привыкаешь. Не так это часто делается.
Ну конечно мне тоже пришла в голову мысль попробовать использовать пищевой температурный датчик. Опускаем его в кастрюльку с пельменями и следим за температурой воды, как только температура приближается к 95-99 градусам, ставим мощность на слабый огонь, пельмени никуда не убегают. Профит. Может как-нибудь попробую. Вроде бы попадались такие датчики в продаже.
Спрашивайте, что не понятно, с радостью отвечу. Спасибо за внимание.
Вот так на меня смотрела кошка, пока я все это делал:
В общем есть электропечь Indesit:
Печь отличная, но в последнее время у нее стали баловаться регуляторы мощности. На двух конфорках работать стали нестабильно и непредсказуемо, невозможно выставить нужную температуру, то выкипает, то совсем гаснет.
Регуляторы мощности там стоят вот такие:
Я пробовал конечно их разбирать, чистил контакты, регулировал, но видимо металл уже изменил своих свойства и толку от этого практически не было.
И тут возникла мысль: «У меня есть Wemos D1 mini, в шаговой доступности есть твердотельные реле, почему нет?»
Итак беру пару вот таких твердотельных реле SSR-10DA:
Они популярные, есть на каждом углу. Есть несколько моделей, в маркировке нас интересуют цифра 10 (ток в амперах) и буквы DA (наша нагрузка будет работать на переменном напряжении, а управлять ею мы будем постоянным напряжением).
Напряжения Wemos D1 mini 3.3V вполне хватает, чтобы закрывать и открывать реле.
Итак, подключаем реле в разрыв питающего конфорку провода (между конфоркой и родным регулятором мощности конфорки). Сделал так на всякий случай, для дополнительной защиты. Т.е. чтобы наш Wemos D1 mini управлял конфоркой, нам нужно вручную ее включить (поставить на макс огонь). На управляющие контакты кидаем провода от пинов Wemos D1 mini (не забываем про полярность), осталось только запилить скетч.
Пока подпаивал пины, вспомнил, что у меня валяются без дела несколько датчиков, остались от старых проектов. И снова «почему нет?».
Подключил к Wemos D1 mini датчик атмосферного давления BMP180 (на борту он кстати имеет и датчик температуры), и датчик температуры и влажности DHT-21. Вот такие:
Подключение описывать не буду, в интернете море информации. Они очень популярные.
Пилим скетч:
Дополнительная информация
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include <Adafruit_BMP085.h>
#include "DHT.h"
#define D0 16 //TDS
#define D1 5 //SCL
#define D2 4 //SDA
#define D3 0
#define D4 2 //LED помпа
#define D5 14 // Земля
#define D6 12 // 2 реле
#define D7 13 // 1 реле
#define D8 15
#define TX 1
#define RX 3
const char* ssid = "Home";
const char* password = "11111111";
String response = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
String response_klimat = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000";
String delimiter = "|";
String delimiter_2 = "@";
String argument = "000000000000000000000000000000000000000";
String argument_name = "000000000000000000000000000000000000000";
String a = "0000000000000000000000";
ESP8266WebServer server(80);
float PERIOD = 10.0;
const float MINIMAL_PERIOD = 0.5;
int power_rate_1 = 0;
int power_rate_2 = 0;
float duration_on_1 = 0;
float duration_on_2 = 0;
float duration_off_1 = 0;
float duration_off_2 = 0;
unsigned long time_on_1;
unsigned long time_on_last_time_1 = 0;
unsigned long time_off_last_time_1 = 0;
unsigned long time_on_2;
unsigned long time_on_last_time_2 = 0;
unsigned long time_off_last_time_2 = 0;
unsigned long timer_1;
unsigned long timer_2;
unsigned long timer_start_1;
unsigned long timer_start_2;
boolean timer_enable_1 = false;
boolean timer_enable_2 = false;
boolean power_on_1 = false;
boolean power_on_2 = false;
boolean enable_1 = false;
boolean enable_2 = false;
//***********HISTORY************************************
const int SIZE_HISTORY = 100;
long HISTORY_PERIOD = 600000;
unsigned long array_millis[SIZE_HISTORY];
float history_temp[SIZE_HISTORY];
float history_temp_dht[SIZE_HISTORY];
float history_baro[SIZE_HISTORY];
float history_hum[SIZE_HISTORY];
unsigned long history_last_time=0;
int history_counter = 0;
//******DHT-21*******************************************
#define DHTPIN 2
#define DHTTYPE DHT21
DHT dht(DHTPIN, DHTTYPE);
float t; //Мгновенная температура
float h; //Мгновенная влажность
float average_t; //Средняя температура
float average_h; //Средняя влажность
float t_array [10] ; //Массив для температуры
float h_array [10] ; //Массив для влажности
int array_counter = 0; //Счетчик переполнения массива
boolean dht_first_flag = 1;
unsigned long dht_last_time =0;
//***********BMP085***************************************
Adafruit_BMP085 bmp;
float baro_temperature;
float baro_pressure;
float average_bar_pressure; //Среднее давление
float average_bar_temperature; //Средняя температура
float bar_temperature_array [10] ; //Массив для температуры
float bar_pressure_array [10] ; //Массив для давления
int bar_array_counter = 0; //Счетчик переполнения массива
boolean bar_first_flag = 1;
unsigned long bmp_last_time;
//--------------------EEPROM----------------------------
#define EE_period 0
#define EE_freq_1 10
#define EE_freq_2 2
//****************************************************************************************
void handleRoot() {
Create_info_response();
server.send(200, "text/html", response_klimat);
}
void handleNotFound(){
String message = "404. Not Found\n\n";
server.send(404, "text/plain", message);
}
void handleSet() {
argument = server.arg(0);
a = server.arg(0);
if(server.argName(0) == "info") {
Create_info_response();
Serial.println(response_klimat);
server.send ( 200, "text/plain", response_klimat );
}
if(server.argName(0) == "power_1") {
int old = power_rate_1;
power_rate_1 = a.toInt();
if(old == 0 && power_rate_1 > 0)time_on_1 = millis();
if(power_rate_1 > 0) enable_1 = true;
else {
enable_1 = false;
power_on_1 = false;
timer_enable_1 = false;
digitalWrite(D7, LOW);
}
Create_info_response();
server.send ( 200, "text/plain", response_klimat );
}
if(server.argName(0) == "power_2") {
int old = power_rate_2;
power_rate_2 = a.toInt();
if(old == 0 && power_rate_2 > 0)time_on_2 = millis();
if(power_rate_2 > 0) {
enable_2 = true;
}
else {
enable_2 = false;
power_on_2 = false;
timer_enable_2 = false;
digitalWrite(D6, LOW);
}
Create_info_response();
server.send ( 200, "text/plain", response_klimat );
}
if(server.argName(0) == "timer_1") {
int value = a.toInt();
if(value > 0) {
timer_1 = value * 60000L;
timer_enable_1 = true;
timer_start_1 = millis();
}else{
timer_enable_1 = false;
}
Create_info_response();
server.send ( 200, "text/plain", response_klimat );
}
if(server.argName(0) == "timer_2") {
int value = a.toInt();
if(value > 0) {
timer_2 = value * 60000L;
timer_enable_2 = true;
timer_start_2 = millis();
}else{
timer_enable_2 = false;
}
Create_info_response();
server.send ( 200, "text/plain", response_klimat );
}
if(server.argName(0) == "klimat") {
response_klimat = "";
response_klimat += average_bar_pressure;
response_klimat += delimiter;
response_klimat += average_bar_temperature;
response_klimat += delimiter;
response_klimat += average_h;
response_klimat += delimiter;
response_klimat += average_t;
server.send ( 200, "text/plain", response_klimat);
}
if(server.argName(0) == "history") {
Create_history_response();
server.send ( 200, "text/plain", response);
}
if(server.argName(0) == "freq") {
int value = a.toInt();
HISTORY_PERIOD = value * 1000L;
EEPROM.begin(10);
int first_byte = value/256;
int second_byte = value%256;
EEPROM.write(EE_freq_1, first_byte);
EEPROM.commit();
EEPROM.write(EE_freq_2, second_byte);
EEPROM.commit();
EEPROM.end();
response_klimat = value;
server.send ( 200, "text/plain", response_klimat);
}
if(server.argName(0) == "period") {
int value = a.toInt();
PERIOD = (float)value;
EEPROM.write(EE_period, (byte)value);
EEPROM.commit();
response_klimat = value;
server.send ( 200, "text/plain", response_klimat);
}
/*
response_klimat = "Get response: ";
response_klimat += server.argName(0);
response_klimat += " Value: ";
response_klimat += a.toInt();
response_klimat += " time:";
response_klimat += millis();
Serial.println(response_klimat);
*/
}
//****************************************************************************************
//****************************************************************************************
void setup(void){
pinMode(D6, OUTPUT);
pinMode(D7, OUTPUT);
pinMode(D5, OUTPUT);
digitalWrite(D5, LOW);
digitalWrite(D6, LOW);
digitalWrite(D7, LOW);
Serial.begin(115200);
Wire.begin();
dht.begin();
bmp.begin();
EEPROM.begin(10);
PERIOD = (float)EEPROM.read(EE_period);
int first_byte = EEPROM.read(EE_freq_1);
int second_byte = EEPROM.read(EE_freq_2);
HISTORY_PERIOD = (first_byte*256+second_byte) * 1000L;
EEPROM.end();
WiFi.begin(ssid, password);
Serial.println("");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/set", handleSet);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
}
//**********************LOOP**************************************************************
//****************************************************************************************
void loop(void){
server.handleClient();
Calc_duration();
Pechka();
Baro(1000);
DHT_21(2000);
History(HISTORY_PERIOD);
Timer();
}
//****************************************************************************************
//****************************************************************************************
void History(long freq){
if((millis()-history_last_time)>freq){
history_temp_dht[history_counter] = average_t;
history_temp[history_counter] = average_bar_temperature;
history_baro[history_counter] = average_bar_pressure;
history_hum[history_counter] = average_h;
array_millis[history_counter] = millis();
history_counter ++;
if(history_counter >= SIZE_HISTORY) history_counter = 0;
history_last_time = millis();
}
}
void Create_history_response(){
response = "";
for(int i = 0; i < SIZE_HISTORY; i++){
response_klimat = "";
response_klimat += history_baro[i];
response_klimat += delimiter;
response_klimat += history_temp[i];
response_klimat += delimiter;
response_klimat += history_hum[i];
response_klimat += delimiter;
response_klimat += history_temp_dht[i];
response_klimat += delimiter;
long m = millis() - array_millis[i];
response_klimat += m;
response_klimat += delimiter_2;
response += response_klimat;
}
}
void Create_info_response(){
response_klimat = "";
response_klimat += power_rate_1;
response_klimat += delimiter;
response_klimat += power_on_1;
response_klimat += delimiter;
if(power_rate_1 > 0) response_klimat += millis() - time_on_1;
else response_klimat += 0;
response_klimat += delimiter;
if(timer_enable_1) response_klimat += timer_start_1 + timer_1 - millis();
else response_klimat += 0;
response_klimat += delimiter;
response_klimat += power_rate_2;
response_klimat += delimiter;
response_klimat += power_on_2;
response_klimat += delimiter;
if(power_rate_2 > 0) response_klimat += millis() - time_on_2;
else response_klimat += 0;
response_klimat += delimiter;
if(timer_enable_2) response_klimat += timer_start_2 + timer_2 - millis();
else response_klimat += 0;
response_klimat += delimiter;
response_klimat += HISTORY_PERIOD;
response_klimat += delimiter;
response_klimat += PERIOD;
}
//---------Барометр-----------------------------------------------------
void Baro(int freq){
if((millis()-bmp_last_time)>freq){
baro_pressure = bmp.readPressure()/133.322;
baro_temperature = bmp.readTemperature();
if(bar_first_flag){
for (int m = 0; m < 10; m++){
bar_temperature_array[m] = baro_temperature;
bar_pressure_array[m] = baro_pressure;
bar_first_flag = 0;
}
}
bar_temperature_array[bar_array_counter] = baro_temperature;
bar_pressure_array[bar_array_counter] = baro_pressure;
bar_array_counter++;
if(bar_array_counter>9){bar_array_counter=0;}
float m1 = 0;
float m2 = 0;
for (int m = 0; m < 10; m++){
m1 = m1 + bar_temperature_array[m];
m2 = m2 + bar_pressure_array[m];}
average_bar_pressure = m2/10.00;
average_bar_temperature = m1/10.00;
bmp_last_time = millis();
}
}
//---------------DHT--------------------------------------------
void DHT_21(int freq){
if ((millis()-dht_last_time)>freq){
t = dht.readTemperature();
h = dht.readHumidity();
if(dht_first_flag){
for (int m = 0; m < 10; m++){
t_array[m] = t;
h_array[m] = h;}
dht_first_flag = 0;
}
t_array[array_counter] = t;
h_array[array_counter] = h;
array_counter++;
if(array_counter>9)array_counter=0;
float summa_t = 0;
float summa_h = 0;
for (int m = 0; m < 10; m++){
summa_t += t_array[m];
summa_h += h_array[m];
}
average_t = summa_t/10.00;
average_h = summa_h/10.00;
dht_last_time = millis();}
}
//---------Pechka-------------------------------------------------------
void Pechka(){
if(enable_1){
if(power_on_1){
if(millis() - time_on_last_time_1 > duration_on_1 * 1000.0){
power_on_1 = false;
time_off_last_time_1 = millis();
digitalWrite(D7, LOW);
//Serial.println("1 off");
}
}
if(!power_on_1){
if(millis() - time_off_last_time_1 > duration_off_1* 1000.0){
power_on_1 = true;
time_on_last_time_1 = millis();
digitalWrite(D7, HIGH);
//Serial.println("1 on");
}
}
}else{
digitalWrite(D7,0);
}
if(enable_2){
if(power_on_2){
if(millis() - time_on_last_time_2 > duration_on_2 * 1000.0){
power_on_2 = false;
time_off_last_time_2 = millis();
digitalWrite(D6, LOW);
//Serial.println("2 off");
}
}
if(!power_on_2){
if(millis() - time_off_last_time_2 > duration_off_2* 1000.0){
power_on_2 = true;
time_on_last_time_2 = millis();
digitalWrite(D6, HIGH);
//Serial.println("1 onn");
}
}
}else{
digitalWrite(D6,0);
}
}
void Calc_duration(){
if(power_rate_1 == 100){
duration_on_1 = 600;
duration_off_1 = 0;
}
if(power_rate_1 == 0){
duration_on_1 = 0;
duration_off_1 = 100000;
}
if(power_rate_1 < 100 && power_rate_1 > 0){
duration_on_1 = power_rate_1/100.0 * PERIOD;
duration_off_1 = PERIOD - duration_on_1;
}
if(power_rate_2 == 100){
duration_on_2 = 600;
duration_off_2 = 0;
}
if(power_rate_2 == 0){
duration_on_2 = 0;
duration_off_2 = 100000;
}
if(power_rate_2 < 100 && power_rate_2 > 0){
duration_on_2 = power_rate_2/100.0 * PERIOD;
duration_off_2 = PERIOD - duration_on_2;
}
}
void Timer(){
if(timer_enable_1){
if(millis() - timer_start_1 > timer_1){
timer_enable_1 = false;
digitalWrite(D7, LOW);
power_rate_1 = 0;
enable_1 = false;
}
}
if(timer_enable_2){
if(millis() - timer_start_2 > timer_2){
timer_enable_2 = false;
digitalWrite(D6, LOW);
power_rate_2 = 0;
enable_2 = false;
}
}
}
Пара слов о скетче.
1. Наш скетч периодически опрашивает датчики. Показания каждого датчика заносятся в массив на 10 значений, после чего вычисляется среднее арифметическое (для большей точности можно вычислять медиану). Соотвественно эти данные о текущих показаниям могут быть в любой момент получены от него.
2. Показания датчиков один раз в час (период можно регулировать) заносятся в массив из 100 значений. И в любой момент этот массив по запросу может быть выдан. Это история показаний датчиков. 100 значений по часу, чуть более 4 суток получается.
3. Алгоритм работы собственно конфорок. Вычисляется время включения и выключения с учетом установленной мощности, установленного цикла. К примеру, длительноть рабочего цикла у нас установлена 10 секунд, а мощность 10%. Следовательно конфорка включится на 1 секунду, затем выключится на 9 секунд и далее по кругу.
4. Запоминаются время включения (для последующего отображения времени готовки) и также устанавливается таймер с обратным отсчетом, о нем позже.
5. Все эти данные могут быть получены с помощью GET-запроса. Ну например, наберем в адресной строке браузера домашнего компьютера следующий адрес:
192.168.0.100/set?info, в ответ получим строку в CSV-формате следующего вида
0|0|0|0|0|0|0|0|3600000|5.00
Числа, разделенные вертикальной чертой, каждое из которых отображает данные о текущем режиме (мощность первой конфорки, время ее работы, таймер, то же для второй, период истории, установленный цикл).
192.168.0.100 — это локальный адрес нашего Wemos D1 mini, он назначается роутером и узнать его можно при подключении Wemos D1 mini к компьютеру в мониторе порта.
192.168.0.100/set?klimat, нам вернет 751.26|21.69|49.16|23.68 — показания датчиков.
192.168.0.100/set?history нам вернет строку длиной почти в 4 тыс. символов примерно в таком виде:
746.28|23.69|54.19|26.14|133351518@746.21|25.09|52.78|25.34|132751517@…
Это показания датчиков с отметкой времени каждого (в ms назад от текущего момента).
Управление будет происходить запросами в виде
192.168.0.100/set?power_1=50 — этот запрос установит мощность первой конфорки на 50%.
Остальные запросы можно посмотреть в скетче.
Вопросы по скетчу можете задавать в комментариях или в личке, отвечу. Зачем там строка длиной в 4500 нулей? Это старая длинная история поиска путей стабильности работы Wemos. К слову, успешная. Wemos немножко неправильно работает с памятью при наращивании строк, и вот эти нули решают эту проблему.
Итак, уже все работает, можем получать информацию и отдавать команды. Осталось только выбрать UI-среду, которая будет делать это чуть более элегантно, чем строчки в браузере. Ну тут у кого к чему душа лежит. Можно сделать локальную веб-страничку с Ajax-запросами. Можно накодить простенькую windows-прогу в WPF. Но разумеется удобнее всего будет смартфон. Я выбрал конечно последнее, тем более, что немножко поверхностно могу кодить на Jave для Android. Кто не может, можете воспользоваться потрясной штукой MIT App Inventor 2. Эта среда позволяет писать проги для Андроида прям на коленке, без знания языка, просто на алгоритмах. В своем прошлом обзоре я пользовался именно ею и подробно о ней рассказывал. Причем результат визуально будет мало отличаться от «взрослой» проги, написанной в Android Studio.
Итак, написанная за пару вечеров программка выглядит так:
На главном экране вверху находятся кнопки выбора конфорки. Поскольку реле подключены к дальней левой и правой ближней, кнопки расположены именно в таком порядке. Кроме этого они служат индикатором текущего режима (меняют цвет от зеленого к красному в зависимости от установленной мощности, белые — если выключены). Для каждой конфорки все параметры (мощность, время, таймер) отдельные.
Ниже время, прошедшее с момента включения конфорки. Удобно сразу видеть, сколько готовится блюдо, без запоминания когда включил и т.д.
Еще ниже таймер. Выполняет 4 функции:
— во-первых, устанавливается ползунком,
— во-вторых, ведет обратный отсчет, показывая сколько осталось,
— в-третьих, выключает конфорку по истечении времени,
— в-четвертых, оповещает хозяина звуковым сигналом и вибрацией по окончании отсчета.
Ниже ползунок для установки мощности в процентах. Еще ниже кнопки для быстрой установки стандартных значений. К примеру, после пары недель использования мы поняли, что суп приятно кипит на 7% мощности, а мясо имеет идеальную золотистую корочку при 53% жарки, можем назначить на кнопки эти предустановленные значения в настройках:
Там же можно регулировать остальные параметры (рабочий цикл, частоту сохранения истории, IP адрес).
В принципе по печке это и все.
В верхнем эпп-баре есть еще пара кнопок (кроме кнопки настройки), средняя ведет нас к этому экрану (текущие показания датчиков):
Температуры две (цифровых), т.к. у нас два датчика, которые ее отображают.
А левая кнопка ведет нас в историю показаний:
Вот график с давлением действительно полезная штука. Тенденция его изменения очень хорошо кореллируется с самочувстсвием.
Пара слов об опыте использования. Супер, что сказать, даже лучше, чем мне казалось вначале. Во-первых, возможность выставить очень точно силу огня, которая будет поддерживаться идеально. Обычные регуляторы так не могут. У них меньше градаций, плюс каждый имеет свой разброс. Угадать не просто. Для блюд, которые хорошо выкипают, это важно.
Во-вторых, время и таймеры. Честно, лично я ленюсь каждый раз засекать время готовки, приходится делать на глаз, пробуя. Плюс частенько поставил, зачитался интересным обзором на Mysku и забыл. А таймер всегда напомнит и выключит огонь. Плюс иногда бывает нужно готовить блюдо несколько часов. Можно поставить и отлучиться в магазин, не переживая, что задержишься.
Дискомфорта от доставания телефона для включения конфорки нет, быстро привыкаешь. Не так это часто делается.
Ну конечно мне тоже пришла в голову мысль попробовать использовать пищевой температурный датчик. Опускаем его в кастрюльку с пельменями и следим за температурой воды, как только температура приближается к 95-99 градусам, ставим мощность на слабый огонь, пельмени никуда не убегают. Профит. Может как-нибудь попробую. Вроде бы попадались такие датчики в продаже.
Спрашивайте, что не понятно, с радостью отвечу. Спасибо за внимание.
Вот так на меня смотрела кошка, пока я все это делал:
+278 |
36284
160
|
Самые обсуждаемые обзоры
+73 |
3550
145
|
+53 |
3719
68
|
+32 |
2754
53
|
как-же закон ома?
перегорание всех электронагревательных приборов происходит в момент пуска, пока температура мала, сопротивление меньше, ток бахает большой…
Нихромовая проволока камфорок про это не знает.
* перегрев -> изгиб ТЭНа/спирали -> касание корпуса нитью -> перегорание нити с/без прогоранием корпуса.
* отгорание провода от контакта нагревателя вследствие некачественной опрессовки/клеммы.
* выгорание описанного пунктом выше контакта из-за ослабления пружинного клемника.
* брак нагревателя в точке соединения контакта и нихромовой нити -> обрыв
* простой обрыв нити спирали. утверждать что спираль оборвалась из-за частых включений нельзя, это были не самые старые ТЭНы, да и их количество было ниже погрешности статистики.
И при чем здесь витамины?
Если ты видел что-то, то это означает только то, что ты увидел то, что хотел увидеть.
Но это не означает, что это могло быть на самом деле.
Если ты не видел просто перегоревшую конфорку или тэн, это не означает что они не перегорают так, как ты не видел.
Так и с витаминами, их никто не видел, а они существуют. ;-))))
в прочем писать ответ я больше не буду, вы мои ответы не читаете в принципе.
правка:
не тому написал
А вот сопротивление вольфрамовой спирали лампы накаливания при нагреве увеличивается на порядок.
Школьная физика.
Можно сделать и лампочки накаливания с нихромовыми спиралями. Они не будут перегорать в момент включения, но свет будет включаться ооочень медленно — примерно, как раскаляется спираль в инфракрасном обогревателе UFO.
Поэтому в лампочках вольфрам (у него: 1. Нужный температурный коэффициент сопротивления — свет загорится быстро. 2. Большая стойкость к высокой температуре — спираль прослужит дольше).
Вообщем, на порядок.
Нихром Х20Н80 — Cr 20 %, Ni 80 %. Удельное сопротивление 1,13 Ом·мм²/м при 20 °C, 1,167 Ом·мм²/м при 1100 °C; максимальная рабочая температура 1200 °C, температура плавления 1400 °C.
Отличная реализация, для кросс платформенности и управления извне можно использовать Blynk. Тогда по запросу из дома, вы хоть с пляжа в Турции сможете включить конфорку.
-это не я!!!
-тьфубл@, плиту взломали.
))))))
это я в шутку, конечно.
Давно в доме плита и духовка на емкостных кнопках
Иначе любой сбой с Wi-Fi оставит вашу семью голодными.
И, да, вы счастливо прошли мимо хитро расположенных граблей: на Али есть фотеки, которые от 3,3 вольт только зажигают свой светодиод, а уверенно включаться начинают лишь от 9 вольт. Это для тех, кто решит повторить самоделку и будет искать причину холодных конфорок при внешне работающей конструкции и весело мигающих индикаторах на фотеке.
PS: Чёт подумалось… Чем дальше цивилизация, тем сложнее становятся элементарные задачи.
Ведь исходный пример: наши деды, чтобы сделать термостат, ставили биметаллический переключатель и ващще не парились. И ведь, по большому счёту, работает же до сих пор, кое-где по полвека аптайм.
Наши отцы, чтобы сделать термостат, паяли кучку транзисторов и резисторов к терморезистору, и получали работоспособность на десятки и десятки лет.
А теперь, прочитал этот фундаментальный обзор и вот на философию потянуло… Вайфайные модули с датчиками атмосферного давления скрещивать. С мобильников/из браузеров обращаться, чтобы плиту включить… ЁмаЁ ©.
Нет, статья познавательная, безусловно. Честный заслуженный плюс поставил.
Но что же будет ещё через пяток лет — даже подумать боязно. Как в «Ералаше», чтобы время на часах посмотреть, два чемодана с собой носить постоянно? :)
Как в девятом элементе.
А линк сделать прямой от esp к мобилке, тогда можно и роутер тушить :)
Не увидел ни одной реальной фотографии реализации. Управление только с телефона? То ещё решение…
Сразу напрашиваются энкодер, дисплей вместо родного…
И прогоняйте текст через spell checker, хоть «конкакты» и «длинной» уберёт...Пока писал пост — сам додумался это «параллельный» стабилизатор тока для разного управляющего входного напряжения…
Никакой сувид будет не нужен :)
Надо развивать идею дальше. MAX6675 с выносным термометром прикрутить, пуш-уведомления на мобилку добавить, рецепты (термопрофили) запилить…
Конечно же неплохо еще поставить термодатчик на конфорку и контроль превышения температуры.
И термодатчики в конфорку встроены по умолчанию, в этом плане порядок.
Думаю, вкупе с твердотельным реле, имеет смысл установить малогабаритный контактор, чтоб он механически отключал питание конфорок.
Если поставить датчики температуры конфорок, то можно организовать ПИД регулятор, будет весьма удобная штука. Конфорка будет разогреваться на максимальной мощности, а потом сама регулировать мощность в зависимости от «теплоотвода».
да и с конфоркой вместо шим удобнее bang-bang алгоритм.
На кухне часто руки бывают не очень чистыми, а нужно срочно отключить/включить/изменить режим комфорки. Как минимум нужно помыть и вытереть руки.
Опять же, смартфон на кухне, если не лежит в кармане подвергается дополнительным рискам: всегда что-то может упасть на него или разлиться.
Как вариант, вскипять воду к моменту как проснусь или сварть яйца интересно.
Алиса, испеки мне пирожки :)
Не всегда это лучше )
1. Отсрочка старта.
2. Поддерживание любой температуры (для брожения например), добавить датчик просто и по его показаниям вкл/выкл конфорку.
3. Детектор закипания (с пищевым датчиком). Прога отслеживает температуру в кастрюле и при достижении температуры кипения автоматически переводит конфорку на слабый огонь.
4. Понаписывать параметры готовки для разных блюд. (Например, жмем «Пельмени», плита включается на 7 минут, «Макароны» — на 10, «Горох» — на 40).
для MIT App Inventor?
Про MIT App Inventor я упомянул, чтоб дать понять, что можно её же написать и без знания Java.
Просто о MIT App Inventor мало кто знает, а штука просто гениальная. Управление любыми устройствами с помощью нее можно перевести в телефон даже не будучи программистом.
Да и фотографировать там нечего, два в реле в плите, управляющая платка в глухой коробке за печью (скрыта от глаз).
пока в мыслях к колонке прикрутить подобный регулятор, но придётся реализовывать его совместно с редуктором и электроприводом…
хотел сделать к кофеварке, чтобы утром нас ждал кофе, но, как верно заметили, жениться проще))))) (а потом жена запрещает курочить кофеварку, ей нравится самой нажимать на кнопку)
да и чем может грозить вращение регулятора?
а вращение регулятора может грозить его поломкой.
Как выработкой ресурса вращения (протрется сильфон итп), так и тупо сворачиванием ему башки.
а вот если часто туда-сюда елозить, то да, можно протереть…
зря, электронные версии-то работают же как-то.
надо подумать.
благодарю за направление :)
автор период вкл. -выкл. 53 сек. делал как на заводском БУ. 10 сек наверное маловато будет.
53 секунды это многовато. Будет то кипеть то не кипеть. А 10 сек как раз, плавно получается. Почему маловато?
Конечно можно, поставить модуль в режим сервера и напрямую подключаться к нему.
Правильно делать вот так:
Вообще такие большие блоки даных нужно строить динамически и отдавать мелкими кусками, а не собирать в единую String строку. Но если это шаманство у вас работает и все устраивает, то ОК, путь живет :)