Рассказ
PowerTech заставил немного по-другому взглянуть на ESP8266, до этого она у меня стояла лишь как приставка к Arduino Mega, служила только для передачи данных на сервер.
Вообще название темы конечно диковато звучит, напоминает фразу "из пушки по воробьям". Но оказывается "пушка" стоит сейчас дешевле рогатки (цена ESP8266 сейчас на уровне простейших Ардуин, немногим больше 2 у.е.).
И создать проращиватель семян это скорее третьестепенная задача. В первую очередь мне хотелось поковырять немного её, запустить на ней сервер, да плюс гекконша снесла яйца, нужно срочно делать для них инкубатор. И только затем уже проращиватель, что по большому счету тоже очень полезная штука, поскольку у меня практически ни одно проращивание не проходит без "случайностей", вечно не знаешь куда притулить семена, то на печку, то на теплый пол, то на батарею, то накроешь, то перебор. В общем нет у меня в квартире места с нужной стабильной температурой. Поэтому от проращивателя явно не откажусь.
Итак, вот герой обзора. Это Wemos D1 mini, по сути это та же ESP8266, только уже со стабилизатором питания, программатором CH340, разъемом для подключения к компу. Никаких головняков.
Программирую в Arduino IDE, мне так привычнее. Вот
статья по настройке среды для ESP8266, ничего сложного.
Вот простейший скетч для поднятия сервера на ней.
Подключаем библиотеки#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
Вводим параметры сети либо для подключения к роутеру в режиме "клиент" (его параметры) либо для создания своей точки доступа в этими параметрами.const char* ssid = "Incubator";
const char* password = "11111111";
Объявляем сервер. здесь важно объявить сервер библиотеки именно ESP8266WebServer.h. Сервер можно сделать и с библиотекой ESP8266WiFi, но с ним я намучался, то виснет, то связь пропадает, а вот с ESP8266WebServer.h работает очень стабильно, ни разу зависаний не было, пару раз пропадала связь, но то возможно из-за роутера, после перезагрузки роутера, связь налаживалась. ESP8266WebServer server(80);
Далее идут функции обработки запросов, которые будут поступать на сервер.
Это функция при попадании на главную страницу сервера, т.е. если в браузере в главной строке введем 192.168.0.13. В ответ сервер посылает код успеха "200", тип ответных данных "text/html", в char temp можно записать любую html-страничкуvoid handleRoot() {
char temp[2000];
server.send(200, "text/html", temp);
}
Обработка "непонятного серверу" запроса. Ошибка 404.void handleNotFound(){
String message = "404. Not Found\n\n";
server.send(404, "text/plain", message);
}
Обработка запроса с параметром /data. Т.е. если в браузере наберем 192.168.0.13/data, то в ответ сервер выдаст не страницу, а просто строку с данными. В данном случае с текущим временем. Удобно для обращения не из браузера, а из, например, программы Андроид-телефона.
void handleData() { String s = (String)millis();
server.send(200, "text/plain", s);
}
Обработка запроса с параметром /set. Аргументы пишутся в таком виде 192.168.0.13/set?temp=30. В данном случае, если наберем в браузере такую строку, то переменная set_temp установится в значение 30. Т.е. мы будем управлять устройством, отдавать команды
void handleSet() {
String a = server.arg(0);
if(server.argName(0) == "temp") {
set_temp = a.toFloat();
}
server.send ( 200, "text/plain", "Ok!" );
}
void setup(void){
А вот здесь идут два блока.
Если написатьэтот, то ESP8266 подключится в качестве клиента к Вашему роутеру. WiFi.begin(ssid, password);
Serial.println("");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
А если этот, то ESP8266 создаст точку доступа (вроде как сам станет роутером) и можно будет подключиться к нему напрямую с телефона. WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print(myIP);
А здесь уже настройка сервера, назначаются функции, описанные выше (они должны идти перед стартом сервера) на разные запросы. server.on("/", handleRoot);
server.on("/data", handleData);
server.on("/set", handleSet);
server.onNotFound(handleNotFound);
Старт сервера server.begin();
}
В главный цикл нужно добавить одну функцию, которая и будет выполнять все задачи.void loop(void){
server.handleClient();
}
Теперь ближе к устройству
Вот такая платка получилась. Датчик температуры LM335 через делитель напряжения на аналоговый вход. Управление печкой через MOSFET-транзистор (от 12 вольт). На качество не обращайте внимания, почти всё паяла дочка (9 лет), почему-то ей это нравится.
Сделали так, чтобы Wemos можно было легко снять, инкубатор нужен по сути только несколько раз в год, а Wemos ковырять хочется всегда
Сама "печка". Просто нихромовая проволока, наклеенная на кусок ДСП. Температура не высокая, сильно не греется, максимум до 40 градусов.
Теперь к интерфейсу. При заходе на главную страницу сервера Wemos выдает мне вот такую простенькую html-страничку
Первая строка - установленная температура, справа поле и кнопка для её изменения.
Вторая - реальная температура, справа поле для коррекции показаний. Если например видим, что датчик немного завышает температуру, то вводим в поле -0.7 и нажимаем кнопку. Температура будет отображаться уже с поправкой.
Третья - мощность работы печки. В программе с помощью ШИМ-модулятора сделал возможность её регулировать. Например, если в конкретных условиях она избыточна, то можно уменьшить, чтоб не было больших перепадов температуры и она более плавно поддерживалась.
Четвертое - время с момента старта.
Все настройки сохраняются в EEPROM, при перезагрузке ничего не сбивается.
Работает очень чётко, поддерживает температуру с точностью до 0.1 градуса.
Вот полный скетч, кому интересно
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
const char* ssid = "DIR-615";
const char* password = "11111111";
ESP8266WebServer server(80);
const int led = 2;
const int pin_pechka = 13;
float set_temp = 28.0;
float real_temp = 30.5;
float correct_temp = 0;
int power = 100;
float array[10];
int ind = 0;
long last_ti;
//****************************************************************************************
void handleRoot() {
char temp[2000];
get_html(temp);
server.send(200, "text/html", temp);
}
void handleNotFound(){
String message = "404. Not Found\n\n";
server.send(404, "text/plain", message);
}
void handleData() {
String s = (String)millis();
server.send(200, "text/plain", s);
}
void handleSet() {
String a = server.arg(0);
if(server.argName(0) == "temp") {
set_temp = a.toFloat();
EEPROM.write(0, (int)set_temp);
EEPROM.write(1, (set_temp-(int)set_temp)*10);
EEPROM.commit();
}
if(server.argName(0) == "correct") {
correct_temp += a.toFloat();
EEPROM.write(2, correct_temp*10+100);
EEPROM.commit();
}
if(server.argName(0) == "power") {
power = a.toInt();
EEPROM.write(3, power);
EEPROM.commit();
}
server.send ( 200, "text/plain", "Ok!" );
}
//****************************************************************************************
//****************************************************************************************
void setup(void){
pinMode(led, OUTPUT);
pinMode(pin_pechka, OUTPUT);
pinMode(A0, INPUT);
Serial.begin(115200);
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());
/*
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.println("AP started:");
Serial.print(myIP);
*/
server.on("/", handleRoot);
server.on("/data", handleData);
server.on("/set", handleSet);
server.onNotFound(handleNotFound);
server.begin();
Serial.println("HTTP server started");
EEPROM.begin(10);
set_temp = EEPROM.read(0) + EEPROM.read(1)/10;
correct_temp = EEPROM.read(2)/10 - 10;
power = EEPROM.read(3);
}
//**********************LOOP**************************************************************
//****************************************************************************************
void loop(void){
long t = millis();
server.handleClient();
if((t-last_ti)>1000){
float y = ((((float)analogRead(A0) / 1023) * 3.3) * 100)-267;
array[ind] = y;
ind++;
if(ind>9) ind=0;
float summ = 0;
for(int n = 0; n<10; n++){
summ += array[n];
}
real_temp=summ/10;
real_temp += correct_temp;
Serial.println((String)real_temp);
last_ti = t;
}
int p = power*2.55;
if(real_temp<set_temp){
analogWrite(pin_pechka, p);
digitalWrite(led, 0);
}else {
analogWrite(pin_pechka, 0);
digitalWrite(led, 1);
}
}
//****************************************************************************************
//****************************************************************************************
void get_html(char *s){
int sec = millis() / 1000;
int min = sec / 60;
int hr = min / 60;
char set_temp_c[10];
dtostrf(set_temp,4, 1, set_temp_c);
char real_temp_c[10];
dtostrf(real_temp,4, 1, real_temp_c);
snprintf ( s, 2000,
"<html>\
<head>\
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\
<title>Инкубатор</title>\
<style type=\"text/css\">\
body {background-color:#565145}\
#glav{width: 500px;margin: 50px auto;font-size: 180%;font-family: sans-serif;text-align: center;}\
.block{width: 100%;height: 70px;margin-top: 10px;padding-top: 10px;background-color: #37434B;box-shadow: 0px 0px 5px #000000;}\
.podpis{float: left;text-align: left;color:#111111;width: 45%;margin-top: 10px;margin-left: 10px;}\
.data{width: 20%;float: left;font-size: 150%;text-shadow: 1px 1px 3px #000;}\
</style>\
</head>\
<body>\
<div id=\"glav\">\
<div class=\"block\">\
<div class=\"podpis\">Set temp:</div>\
<div class=\"data\" style=\"color: #66D110\">%s</div>\
<input id=\"inp_temp\" type=\"text\" size=\"2\">\
<button style=\"background-color: #E26408; border-radius: 10px\" onclick=\"document.location.href = 'http://192.168.0.13/set?temp='+ document.getElementById('inp_temp').value;\">Set</button>\
</div>\
<div class=\"block\">\
<div class=\"podpis\">Real temp:</div>\
<div class=\"data\" style=\"color: #EE710C\">%s</div>\
<input id=\"cor_temp\" type=\"text\" size=\"2\">\
<button style=\"background-color: #E26408; border-radius: 10px\" onclick=\"document.location.href = 'http://192.168.0.13/set?correct='+ document.getElementById('cor_temp').value;\">Set</button>\
</div>\
<div class=\"block\">\
<div class=\"podpis\">Power:</div>\
<div class=\"data\" style=\"color: #E5E200\">%d\%</div>\
<input id=\"inp_power\" type=\"text\" size=\"2\">\
<button style=\"background-color: #E26408; border-radius: 10px\" onclick=\"document.location.href = 'http://192.168.0.13/set?power='+ document.getElementById('inp_power').value;\">Set</button>\
</div>\
<div class=\"block\">\
<div class=\"podpis\">Time:</div>\
<div class=\"data\" style=\"color: #75718F; font-size: 80%; margin-top: 15px\">%d:%02d:%02d</div>\
</div>\
</div>\
</body>\
</html>",
set_temp_c,real_temp_c,power,hr, min % 60, sec % 60
);
}
Думал еще сделать прогу на Андроиде, там не сложно, но затем решил, что необходимость её нулевая. Управлять не так часто нужно, браузера с головой хватит.
Да, и чуть не забыл, олово и флюс, использованное в проекте, от
kido, за что ему мерси.