Термостат для скважины с WiFi и thingspeak v2.0 (ESP-07 + DS18B20 4шт. + реле)
- Войдите на сайт для отправки комментариев
Данный контроллер создавался для автоматического управления обогревом домика с насосным оборудованием скважины с возможностью удаленного ручного управления и удаленного контроля за температурой .
Оборудование скважины расположено не в колодце, а в утепленном домике на поверхности земли. На зимовку уходит второй раз и мне хочеться понять, насколько хорошо утеплен сам домик и до какой температуры хватит тепла из самой скважины, чтобы поддерживать плюсовую температуру в домике, который утеплен 3 слоями пенопласта.
Обогрев домика реализован в 2 вариантах:
- саморегулируемый греющий кабель (~70Вт), который может греть трубу в земле на глубину 2 метра этот кабель включается вручную
- 60 ваттная лампочка накаливания внутри домика, которая включается через реле, подключенное к ESP и управляется как
автоматически, так и вручную
Основная функция контроллера - следить за температурой воздуха в домике и включать/выключать реле, к которому подключен обогрев (лампочка накаливания). Накопления данных на контроллере не происходит, т.к. подразумевается, что данные через 3g-роутер отправляются на сервер thingspeak.com. Первая версия контроллера отличалась отсутствием gsm связи и была написана с локальным накоплением данных и даже с рисованием графиков через web-интерфейс. Но желание иметь удаленное управление потребовало использование 3g-роутера и сделало возможным использование сторонних сервисов типа thingspeak.com. Это позволило не заморачиваться с локальной обработкой и накоплением данных, а так же отключить модуль часов DS3231. Чтобы иметь удаленное управление пришлось купить симку с реальным ip-адресом, но т.к. статический ip-адрес стоит дорого, то симка куплена с димнамическим ip, что в свою очередь потребовало использование сервиса типа dyndns.com.
Кстати, для информации - у МТС есть хорошй тариф для интернета вещей - 500 рублей в год, 200мб/мес, 200смс.
Если связь с роутером пропадает, то ESP сама переключается в режим точки доступа и к ней можно подключаться для доступа к web-интерфейсу. Если роутер вновь становиться доступным, то ESP снова подключается к нему. В обеих случаях ESP имеет один и тот же статический ip (речь про локальную сеть).
Не успел сделать загрузку скетча по OTA. Т.к. контроллер уже закрыт на зиму в домике и доступа к нему нет, то это будет в следующем сезоне.
Функции контроллера:
1 зона контроля температуры с автоматическим управлением реле по заданным уставкам
3 дополнительные зоны контроля температуры (мне любопытно понаблюдать за температурой земли под домиком, температурой на улице и температурой греющего кабеля)
авторизация для входа на web-интерфейс
отправка данных на сервер thingspeak.com
статус соединения с сервером thingspeak
отображение текущей температуры с 4 датчков
отображение температуры включения/выключения реле
изменение температуры включения/выключения реле
отображение состояния реле (включено/выключено)
ручное изменение состояния реле (вкл/выкл)
отображение времени работы контроллера после включения
отображение количества включений реле
отображение уровня wifi сигнала
отображение цикла опроса датчиков и отправки данных на сервер (частота опроса в минутах)
изменение цикла опроса и отправки
В Мониторе порта можно увидеть полную диагностическую информацию о работе контроллера.
Скетч написан для 1 реле и 4х датчиков ds18b20, но леко может быть изменен на требуемое вам количество датчиков.
Если датчиков будет меньше или реле будет отсутствовать - никаких изменений не требуется.
Использованные железки:
ESP-07
Ultra-small DC-DC step-down module
плата 5*7см
wifi антенна
4 датчика ds18b20
1 модуль реле
//на сотовом в Опере для включения-выключени необходимо перезагружать страницу "ВКЛЮЧИЛ!" или "ВЫКЛЮЧИЛ!" #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <OneWire.h> //для ds18b20 #include <DallasTemperature.h> //для ds18b20 #define PIN_RELAY 12 //для реле #define ONE_WIRE_BUS 4 //для ds18b20 OneWire oneWire(ONE_WIRE_BUS); //для ds18b20 DallasTemperature sensors(&oneWire); //для ds18b20 DeviceAddress deviceAddress; //для ds18b20 char thingspeakStatus[15] = "..."; //строка статуса соединения с ThingSpeak "connect" или "fail" String TimeStr; //строка для вывода SystemUp получаемая из millis() в TimePrint int SignalQuality = 0; //для вывода качества wifi сигнала на html страницу int numdat; //для ds18b20 - кол-во датчиков int ReleStateNeedChange = 0; //включение-выключение реле через страницу - дважды нажать, ибо с первого раза не срабатывает unsigned NumOfReleTimes = 0; //кол-во включений реле после включения контроллера float CurrentTemp1 = 77; //температура 1 датчика float CurrentTemp2 = 77; // float CurrentTemp3 = 77; // float CurrentTemp4 = 77; // //-- переменные для работы с реле int ReleState = 0; //состояние реле 1-вкл, 0-выкл float ReleTempOn = 1; //Температура включения реле float ReleTempOff = 7; //Температура выключения реле //-- переменные для работы с временными циклами (опрос датчиков, проверка режима WiFI) const int PeriodWiFi = 10; //период в минутах соединения с роутером int OprosTime = 3; //период в минутах опроса счетчиков const int PeriodAP = PeriodWiFi * 60 * 1000; //период через который пытаемся соединиться с WiFi-роутером в миллисекундах. int PeriodOprosa; //период опроса датчиков в миллисекундах. 1000мсек=1сек unsigned long next_time; //время очередного опроса счетчиков, получаем из millis() unsigned long next_time2; //время очередного сканирования сети и отправки на thingspeak, получаем из millis() //переменные для поднятия точки доступа/клиента WiFi ESP8266WebServer server(8080); //порт по которому отвечает web-сервер //сетевые настройки для режима AP. в режиме Client этот же адрес получаем от роутера - прописываем MAC-адрес нашей esp в настройках DHCP->Static Leases IPAddress local_IP(192, 168, 3, 99); //ip-адрес, по которому отвечает наша ESP и в режиме AP,в режиме Client адрес тот же но делается это в настройках роутера IPAddress gateway(192, 168, 3, 1); //адрес WiFI-роутера, с которым пытается соединиться esp в режиме Client IPAddress subnet(255, 255, 255, 0); // const char *ssid = "YYY"; //имя сети, к которой пытается приконнектится esp в режиме Client const char *ssidAP = "xxx"; //этот имя wifi-сети, которую создает esp в режиме AP (точки доступа) const char *password = "xyz"; //пароль WiFi-роутера с которым соединямеся, этот же пароль используем в режиме AP //переменные для работы с режимами WiFi (точка доступа/клиент) и переключения между ними bool APmodeON = false; //флаг, что ESP сама стала точкой доступа bool CLmodeON = false; //флаг, что ESP соединена с WiFi-роутером bool ChangeWiFi2AP = false; //надо менять режим на AP bool ChangeWiFi2CL = false; //надо менять режим на WiFiClient //переменные для паролирования доступа к web-серверу const char *www_username = "xxx"; //имя для доступа на html страницу const char *www_password = "yyy"; //пароль для доступа на html страницу const char *www_realm = "Login Required"; String authFailResponse = "Authentication Failed"; //--- переменные для работы с сервисом Thingspeak.com const char * myWriteAPIKey = "XXXXXXXXXXXXXXXX"; //Thingspeak.com Write API Key unsigned long myChannelNumber = 111111; //Thingspeak.com Channel ID const char* host = "api.thingspeak.com"; //Thingspeak.com address //------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------ void setup() { Serial.begin(57600); delay(500); next_time = millis(); //текущее время, нужно для первого старта опроса счетчиков в теле loop void next_time2 = millis(); //текущее время, нужно для первого старта сканирования сети //инициализация библиотеки и выставления точности на датчках ds18b20 sensors.begin(); numdat = sensors.getDeviceCount(); for (int i = 0; i < numdat; i++) { sensors.getAddress(deviceAddress, i); sensors.setResolution(deviceAddress, 12);} //задание обработчиков обращений через web server.on("/", handleRoot); server.on("/ROn", handle_ROn); //включение реле server.on("/ROff", handle_ROff); //выключение реле server.on("/RMode", handle_RMode); //страница выбора вкл/выкл реле server.on("/TOnm", handle_TOnm); //температура включения реле плюс один градус server.on("/TOnp", handle_TOnp); //температура включения реле минус один градус server.on("/TOffm", handle_TOffm); //температура выключения реле плюс один градус server.on("/TOffp", handle_TOffp); //температура выключения реле минус один градус server.on("/Oprpl", handle_Oprpl); //изменение времени цикла опроса датчиков server.on("/Oprmin", handle_Oprmin); server.begin(); Serial.println("HTTP server started"); pinMode(PIN_RELAY, OUTPUT); // Объявляем пин реле как выход digitalWrite(PIN_RELAY, HIGH); // Выключаем реле - посылаем высокий сигнал } //------------------------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------------------------ void loop() { PeriodOprosa = OprosTime*60*1000; //вычисляем период опроса счетчиков и отправки данных на thingspeak. т.к. оно может изменяться через web, то вычисляем его int now_time = millis(); int now_time2 = millis(); //текущее время if( now_time >= next_time ) { next_time = now_time + PeriodAP; TestWiFiConnection();} //Периодическая проверка подключения к WiFi -в каком режиме находится ESP - AP или Client? Если доступен WiFi-роутер - остаемся на соединении с ним или вновь соединяемся с ним. Если не доступен - становимся точкой доступа //опрашиваем все датчики, отправляем на Thingspeak.com if( now_time2>= next_time2 ) //Если прошло время равное или больше PeriodOprosa, запускаем опрос { //ThingSpeak will only accept updates every 15 seconds. TimePrint(); //вычисляем время прошедшее с включения контроллера и выводим в serial next_time2 = now_time2 + PeriodOprosa; //устанавливаем следующее время опроса getTempDS18B20(); if (CurrentTemp1 <= ReleTempOn and ReleState == 0) {Serial.println("ВКЛЮЧАЮ по температуре"); ReleOn();} if (CurrentTemp1 > ReleTempOff and ReleState == 1) {Serial.println("ВыКЛЮЧАЮ реле по температуре"); ReleOff();} SendData2ThingSpeakViaWiFi(); Serial.print("Следующий опрос датчиков через "); Serial.print(OprosTime);Serial.println(" минуты"); Serial.println(""); } server.handleClient(); //локальный web-сервер //обработка включения-выключения через html-страницу if (ReleStateNeedChange == 1 and ReleState == 0) { Serial.println("---Включаю по команде с web!---"); ReleOn();} // Включаем реле - посылаем низкий уровень сигнала if (ReleStateNeedChange == 1 and ReleState == 1) { Serial.println("---Выключаю по команде с web!---"); ReleOff();} // Отключаем реле - посылаем высокий уровень сигнала } //------------------------------------------------------------------------------------------------------ //------ Конец основного цикла-------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------------ void SendData2ThingSpeakViaWiFi() // ThingSpeak will only accept updates every 15 seconds. { /*Data, Time, Temp1, Temp2, Temp3, Temp4, Press fld1 fld2 fld3 fld4 fl25 fl6 fld7 Write to ThingSpeak. There are up to 8 fields in a channel, allowing you to store up to 8 different pieces of information in a channel.*/ Serial.print("SendData2ThingSpeak - Connecting to "); Serial.println(host); WiFiClient client; // Use WiFiClient class to create TCP connections const int httpPort = 80; if (!client.connect(host, httpPort)) {Serial.println("SendData2ThingSpeak - Connection to ThingSpeak.com failed !"); snprintf (thingspeakStatus,8,"fail"); return; } else {Serial.println("SendData2ThingSpeak - connected to ThingSpeak.com"); snprintf (thingspeakStatus,15,"connect");} // Создаем URI для запроса String url = "/update?key="; url+= myWriteAPIKey; url+= "&field1="; url+= CurrentTemp1; url+="&field2="; url+= CurrentTemp2; url+="&field3="; url+= CurrentTemp3; url+="&field4="; url+= CurrentTemp4; //url+="&field5="; url+= CurrentPressure; Serial.print("SendData2ThingSpeak - Requesting URL: "); Serial.print(host); Serial.println(url); // отправляем запрос на сервер client.print(String("GET ") + url + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); client.flush(); // ждем отправки всех данных // Read all the lines of the reply from server and print them to Serial while(client.available()) { String line = client.readStringUntil('\r'); //char line = client.read(); Serial.print(line); Serial.println(); Serial.println("closing connection"); } Serial.println(""); } //------------------------------------------------------------------------------------------- //------------- для ds18b20 функции начало -------------------------------------------------- //------------------------------------------------------------------------------------------- void getTempDS18B20() //опрос счетчиков { Serial.print("getTempDS18B20 - Found "); Serial.print(sensors.getDeviceCount()); Serial.println(" ds18b20 devices."); sensors.requestTemperatures(); delay(500); for (int i = 0; i < numdat; i++) { sensors.getAddress(deviceAddress, i); if (i == 0) {CurrentTemp1 = sensors.getTempC(deviceAddress);} if (i == 1) {CurrentTemp2 = sensors.getTempC(deviceAddress);} if (i == 2) {CurrentTemp3 = sensors.getTempC(deviceAddress);} if (i == 3) {CurrentTemp4 = sensors.getTempC(deviceAddress);} Serial.print("Sensor "); Serial.print(i); Serial.print(": "); Serial.print(sensors.getTempC(deviceAddress)); Serial.print(". Address: "); printAddress(deviceAddress); Serial.println(""); } Serial.print("Rele state: "); Serial.print(ReleState); Serial.print("; T.вкл: "); Serial.print(ReleTempOn); Serial.print("; T.выкл: "); Serial.println(ReleTempOff); Serial.println(); } //------------------------------------------------------------------------------------------- //------------- WiFi функции начало --------------------------------------------------------- //------------------------------------------------------------------------------------------- void WIFIAP_Client() { Serial.print("WIFI_Client - SSID: "); Serial.print(ssid); Serial.print(" PASS: "); Serial.println(password); Serial.print("WIFI_Client - Trying connection to WiFi router"); WiFi.disconnect(); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); // WiFi.config(local_IP, gateway, subnet); //Установить определенный ip-адрес, иначе получает адрес по DHCP uint8_t CountOfConnectTime = 0; while (WiFi.status() != WL_CONNECTED && CountOfConnectTime++ < 20) { Serial.print ("."); delay(500);} //ожидание соединения с роутером Serial.println(""); if (WiFi.status() == WL_CONNECTED) { Serial.println ( "WIFI_Client - ESP connected to WiFi router" );} else { Serial.println ( "WIFI_Client - ESP NOT connected to WiFi router, ChangeWiFi2AP = true;" ); ChangeWiFi2AP = true;} Serial.println(""); } bool StartAPMode() { WiFi.disconnect(); WiFi.mode(WIFI_AP); WiFi.softAPConfig(local_IP, gateway, subnet); WiFi.softAP(ssidAP,password); APmodeON = true; CLmodeON = false; Serial.print("StartAPMode - ESP switched to AP Mode;"); Serial.print(" SSID: "); Serial.print(ssidAP); Serial.print("; PASS: "); Serial.print(password); Serial.print("; IP address: "); Serial.println(WiFi.softAPIP()); Serial.println(); return true; } bool WIFI_Scan_for_ssid() { int n = WiFi.scanNetworks(); bool netfound = false; Serial.print("WIFI_Scan - WiFi Scan done - "); if (n == 0) Serial.println("no networks found"); else { Serial.print(n); Serial.println(" networks found: "); for (int i = 0; i < n; ++i) // Вывести в цикле в терминал список всех найденных сетей, чем меньше цифра качества сигнала-тем лучше { if (WiFi.SSID(i) == ssid) {netfound = true; SignalQuality = WiFi.RSSI(i) ;} Serial.print(WiFi.SSID(i)); Serial.print(":"); Serial.print(WiFi.RSSI(i)); Serial.print("; "); Serial.print((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":"*"); } Serial.println(""); if (netfound) {Serial.print("WIFI_Scan - Found WiFi network with SSID - "); Serial.print(ssid); Serial.print("; Signal Quality: "); Serial.println(SignalQuality); Serial.println(""); netfound = false; return true;} else {Serial.print("WIFI_Scan - WiFi network with SSID - "); Serial.print(ssid); Serial.println(" - NOT FOUND!"); Serial.println(""); return false;} } } void TestWiFiConnection() //проверяет подключение к wifi сети, если соединение утеряно, пытается подключиться снова или переключает ESP в режим Access Point { Serial.print ( "TestWiFi - APmodeON: " ); Serial.print (APmodeON); Serial.print ( "; CLmodeON: " );Serial.print (CLmodeON); Serial.print ( "; ChangeWiFi2CL: " );Serial.print (ChangeWiFi2CL); Serial.print ( "; ChangeWiFi2AP: " );Serial.println (ChangeWiFi2AP); Serial.print("TestWiFi - Current mode: "); if (APmodeON) { Serial.println("Access Point");} if (CLmodeON) { Serial.println("WiFi Client");} if ((CLmodeON and APmodeON) or (!CLmodeON and !APmodeON)) { Serial.println("Not available");} if (WiFi.status() != WL_CONNECTED and !APmodeON and !CLmodeON) { Serial.println ("TestWiFi - WiFi initialization"); ChangeWiFi2CL = true; } if (!WIFI_Scan_for_ssid()) { if (WiFi.status() != WL_CONNECTED and CLmodeON){ Serial.println ("TestWiFi - Connection to WiFi router is lost"); ChangeWiFi2AP = true; ChangeWiFi2CL = false;} //если нет соединения и мы в режиме Client - соединение с роутером потеряно if (APmodeON) { Serial.println("TestWiFi - ESP will continue to work in AP mode"); ChangeWiFi2CL = false;} } else { if (APmodeON){ Serial.println ("TestWiFi - ESP in AP mode. Switching to Client mode"); ChangeWiFi2CL = true; } } if (WiFi.status() == WL_CONNECTED and CLmodeON){ Serial.println ("TestWiFi - ESP connected to WiFi router - reconnection it is not necessary"); ChangeWiFi2CL = false; ChangeWiFi2AP = false; } if (ChangeWiFi2CL) //попытка соединения с WiFi-роутером { WIFIAP_Client(); if (WiFi.status() == WL_CONNECTED) {CLmodeON = true; APmodeON = false ; ChangeWiFi2AP = false;} if (WiFi.status() == WL_CONNECTED) {Serial.print("TestWiFi - AP assigned to ESP IP address: "); Serial.println(WiFi.localIP());} if (WiFi.status() != WL_CONNECTED and !APmodeON) {ChangeWiFi2AP = true; Serial.println ( "TestWiFi - Don't connect to WiFi router, switch to AP mode!" );} ChangeWiFi2CL = false; } if (ChangeWiFi2AP) //если нет связи с роутером - становимся точкой доступа { StartAPMode(); ChangeWiFi2AP = false; } Serial.print ( "TestWiFi - APmodeON: " );Serial.print (APmodeON); Serial.print ( "; CLmodeON: " );Serial.print (CLmodeON); Serial.print ( "; ChangeWiFi2CL: " );Serial.print (ChangeWiFi2CL); Serial.print ( "; ChangeWiFi2AP: " ); Serial.println (ChangeWiFi2AP); Serial.println(""); } //------------------------------------------------------------------------------------------- //------------- WiFi функции конец ---------------------------------------------------------- //------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------- //---------------------- Обработчики и вывод web-страниц ------------------------------------ //------------------------------------------------------------------------------------------- void handleRoot() { Serial.println ("handleRoot - Запрос на авторизацию "); if(!server.authenticate(www_username, www_password)) {Serial.println ("handleRoot - Ошибка авторизации "); return server.requestAuthentication(DIGEST_AUTH, www_realm, authFailResponse);} Serial.println("handleRoot - Авторизация успешна "); Serial.println("handleRoot - Send HTMLpage "); //Переменные для формирования html страницы char buffer1[12]; char buffer2[12]; char buffer3[12]; char buffer4[12]; char buffer5[12]; char buffer6[12]; char str_temp[6]; //Температура char tempst[38];char tempst2[18]; //Статус реле char temp[700]; //Текст страницы char temp2[20]; //Время работы dtostrf(CurrentTemp1, 3, 1, str_temp); sprintf(buffer1,"%s", str_temp); dtostrf(CurrentTemp2, 3, 1, str_temp); sprintf(buffer2,"%s", str_temp); dtostrf(CurrentTemp3, 3, 1, str_temp); sprintf(buffer3,"%s", str_temp); dtostrf(CurrentTemp4, 3, 1, str_temp); sprintf(buffer4,"%s", str_temp); dtostrf(ReleTempOn, 3, 1, str_temp); sprintf(buffer5,"%s", str_temp); dtostrf(ReleTempOff, 3, 1, str_temp); sprintf(buffer6,"%s", str_temp); TimeStr.toCharArray(temp2, 20); if (ReleState == 0) {snprintf ( tempst, 34, "<a href=./ROn>Включить"); snprintf ( tempst2, 18, "Выключен"); } else {snprintf ( tempst, 38, "<a href=./ROff>Выключить"); snprintf ( tempst2, 16, "Включен");} snprintf ( temp, 700, "<html><head><meta http-equiv='сache-сontrol' content='no-cache'><meta charset='utf-8'/></head><body>\ На улице: %s<br>\ В домике: %s<br>\ Кабель: %s<br>\ Земля: %s<br>\ <a href=./RMode>Обогрев</a>: <b>%s</b><br>\ Включений - %i<br><hr>\ <a href=./TOnm> -- - </a> Т.вкл: <b>%s</b> <a href=./TOnp> - ++ </a><br>\ <a href=./TOffm> -- - </a> Т.выкл: <b>%s</b> <a href=./TOffp> - ++ </a><br>\ <a href=./Oprmin> -- - </a> Датчики(мин): %i <a href=./Oprpl> - ++ </a><br>\<br><hr>\ WiFiTest(мин): %i<br>\ SystemUp: %s<br>\ WiFiSignal: %i<br>\ thingSpeak: %s<br>\ </body></html>", buffer4, buffer1, buffer2, buffer3, tempst2, NumOfReleTimes, buffer5, buffer6, OprosTime, PeriodWiFi, temp2, SignalQuality, thingspeakStatus); server.send ( 700, "text/html", temp ); } //-------------------------------------------- void handle_TOnp () //страница изменения температуры включения реле { char temp[200]; //Текст страницы ReleTempOn = ReleTempOn +1; if (ReleTempOn >= ReleTempOff) {ReleTempOn = ReleTempOn -1;} snprintf ( temp, 200, "<HTML>\ <HEAD>\ <META HTTP-EQUIV='REFRESH' CONTENT='1; URL=./'>\ </HEAD>\ <BODY>\ </BODY>\ </HTML>"); server.send ( 200, "text/html", temp ); } //-------------------------------------------- void handle_TOnm () //страница изменения температуры включения реле { char temp[200]; //Текст страницы ReleTempOn = ReleTempOn -1; snprintf ( temp, 200, "<HTML>\ <HEAD>\ <META HTTP-EQUIV='REFRESH' CONTENT='1; URL=./'>\ </HEAD>\ <BODY>\ </BODY>\ </HTML>"); server.send ( 200, "text/html", temp ); } //-------------------------------------------- void handle_TOffp () //страница изменения температуры включения реле { char temp[200]; //Текст страницы ReleTempOff = ReleTempOff +1; snprintf ( temp, 200, "<HTML>\ <HEAD>\ <META HTTP-EQUIV='REFRESH' CONTENT='1; URL=./'>\ </HEAD>\ <BODY>\ </BODY>\ </HTML>"); server.send ( 200, "text/html", temp ); } //-------------------------------------------- void handle_TOffm () //страница изменения температуры включения реле { char temp[200]; //Текст страницы ReleTempOff = ReleTempOff -1; if (ReleTempOff <= ReleTempOn) {ReleTempOff = ReleTempOff +1;} snprintf ( temp, 200, "<HTML>\ <HEAD>\ <META HTTP-EQUIV='REFRESH' CONTENT='1; URL=./'>\ </HEAD>\ <BODY>\ </BODY>\ </HTML>"); server.send ( 200, "text/html", temp ); } //-------------------------------------------- void handle_RMode () //страницы выбора - включить/отключить обогрев { char tempst2[18]; //Статус реле char temp[700]; //Текст страницы if (ReleState == 0) {snprintf ( tempst2, 18, "ВыКЛЮЧЕН");} else {snprintf ( tempst2, 16, "ВКЛЮЧЕН");} snprintf ( temp, 700, "<html><head><meta http-equiv='сache-сontrol' content='no-cache'><meta charset='utf-8'/></head><body><center>\ <table border=1>\ <tr><td>Управление обогревом</td><td></td></tr>\ <tr><td> </td><td></td></tr>\ <tr><td>Текущий статус - %s</td><td></td></tr>\ <tr><td> </td><td></td></tr>\ <tr><td><a href=./ROn>Включить резервный обогрев</a></td><td></td></tr>\ <tr><td> </td><td></td></tr>\ <tr><td><a href=./ROff>Отключить резервный обогрев</a></td><td></td></tr>\ <tr><td> </td><td></td></tr>\ <tr><td><a href=./>Вернуться</a></td><td></td></tr>\ </table></body></html>", tempst2); server.send ( 400, "text/html", temp ); } //-------------------------------------------- void handle_ROff () { ReleStateNeedChange = 1; Serial.println("handle_ROff - Пришла команда отключения со страницы html"); char temphtm[400]; snprintf ( temphtm, 400, "<html><head><meta http-equiv='Pragma' content='no-cache'><meta http-equiv='сache-сontrol' content='no-cache'><meta charset='utf-8'/></head>\ <body><center>\ <table border=1>\ <tr><td><h1>Отправил команду ВЫКЛЮЧИТЬ!</td></tr>\ <tr><td><h1><a href=./>Вернуться</a></h1></td></tr>\ <tr><td><h1>_</h1></td></tr>\ <tr><td><h1>В некоторых бразуерах требуется обновить эту страницу, чтобы команда сработала</h1></td></tr>\ </table></center></body></html>"); server.send(400, "text/html", temphtm); } //-------------------------------------------- void handle_ROn () { ReleStateNeedChange = 1; Serial.println("handle_ROn - Пришла команда включения со страницы html"); char temphtm[400]; snprintf ( temphtm, 400, "<html><head><meta http-equiv='Pragma' content='no-cache'><meta http-equiv='сache-сontrol' content='no-cache'><meta charset='utf-8'/></head>\ <body><center>\ <table border=1>\ <tr><td><h1>Отправил команду ВКЛЮЧИТЬ!!!</h1></td></tr>\ <tr><td><h1><a href=./>Вернуться</a></h1></td></tr>\ <tr><td><h1>_</h1></td></tr>\ <tr><td><h1>В некоторых бразуерах требуется обновить эту страницу, чтобы команда сработала</h1></td></tr>\ </table></center></body></html>"); server.send(400, "text/html", temphtm); } //-------------------------------------------- void handle_Oprpl () //страница изменения времени цикла опроса датчиков { char temp[200]; //Текст страницы OprosTime = OprosTime +1; snprintf ( temp, 200, "<HTML>\ <HEAD>\ <META HTTP-EQUIV='REFRESH' CONTENT='1; URL=./'>\ </HEAD>\ <BODY>\ </BODY>\ </HTML>"); server.send ( 200, "text/html", temp ); } //-------------------------------------------- void handle_Oprmin () //страница изменения времени цикла опроса датчиков { char temp[200]; //Текст страницы OprosTime = OprosTime -1; snprintf ( temp, 200, "<HTML>\ <HEAD>\ <META HTTP-EQUIV='REFRESH' CONTENT='1; URL=./'>\ </HEAD>\ <BODY>\ </BODY>\ </HTML>"); server.send ( 200, "text/html", temp ); } //---------------------------------------------------------------------------------------------- void ReleOn() { digitalWrite(PIN_RELAY, LOW); NumOfReleTimes = NumOfReleTimes + 1; //Количество включений реле ReleState = 1; ReleStateNeedChange = 0; Serial.println("ReleOn - Реле включено! ReleState = 1"); Serial.println(); } void ReleOff() { digitalWrite(PIN_RELAY, HIGH); ReleState = 0; ReleStateNeedChange = 0; Serial.println("ReleOff - Реле вЫключено! ReleState = 0"); Serial.println(); } //---------------------------------------------------------------------------------------------- void TimePrint() { unsigned long Time = millis(); int Day = 0; int Hours = 0; int Min = 0; int Sec = 0; String DayStr = "00"; String HoursStr = "00"; String MinStr = "00"; String SecStr = "00"; if (Time > 86400000) {Day = Time/86400000; DayStr = String(Day);} //сутки if (Time > 3600000) {Hours = (Time - Day*86400000)/3600000; HoursStr = String(Hours);} //часы if (Time > 60000) {Min = (Time - Day*86400000 - Hours*3600000)/60000; MinStr = String(Min);} //минуты if (Time < 60000) {Sec = Time/1000; SecStr = String(Sec);} //секунды TimeStr = DayStr + " дней " + HoursStr + ":" + MinStr + ":" + SecStr; Serial.print ("TimePrint - Работает "); Serial.println (TimeStr); } //---------------------------------------------------------------------------------------------- void printAddress(DeviceAddress deviceAddress) { for (uint8_t i = 0; i < 8; i++) { // zero pad the address if necessary if (deviceAddress[i] < 16) Serial.print("0"); Serial.print(deviceAddress[i], HEX); } }
Выявил глюк в Opere для Android - при нажатии на ссылку включения/выключения реле, как положено вы попадает на страницу, которая сообщает, что реле включено/выключено, но на самом деле ничего не происходит. Нужно обновить страницу, где будет написано "Выключил" ("Выключено"), тогда реле сработает.
Связываю это с кешированием данных в браузере, т.к. когда включаешь/выключаешь реле в первый раз - все отрабатывает нормально.
Добавление на страницу тега, который говорит браузеру он том, что кешировать не надо - не помогло.
Из Firefox работает нормально.
Баги, изначально присутствующие в коде:
При вычислении времени работы через millis() происходит накопление ошибки, но оно мало и для тех задач, которые выполняет контроллер, это не несет никакого вреда.
Плюс примерно через 50 дней работы без перезагрузки из-за переполнения этого millis() возможно перестанет работать опрос счетчиков :) но пока решил не бороться с этим, т.к. минимум раз в месяц контроллер будет перегружаться по питанию.
Я поставил вам плюс, но, извините, слушать про переполнение millis'a уже надоело.....
Плюс примерно через 50 дней работы без перезагрузки из-за переполнения этого millis() возможно перестанет работать опрос счетчиков :) но пока решил не бороться с этим, т.к. минимум раз в месяц контроллер будет перегружаться по питанию.
Проблемы переполнения millis не существует. Достаточно правильно высчитывать интервалы. Обсасывалось сто раз на форуме, просто повторюсь вкратце. Вот так - неправильно:
А вот так - правильно:
И никакой проблемы.
Я поставил вам плюс, но, извините, слушать про переполнение millis'a уже надоело.....
Ну за что купил, за то продал, прошу прощенья, если его нет, то отлично...
Ну за что купил, за то продал, прошу прощенья, если его нет, то отлично...
из этой фразы можно сделать вывод, что скетч не ваш, а найден в инете. Если это так - это надо написать и указать ссылку, откуда брали.
Ну за что купил, за то продал, прошу прощенья, если его нет, то отлично...
из этой фразы можно сделать вывод, что скетч не ваш, а найден в инете. Если это так - это надо написать и указать ссылку, откуда брали.
из этой фразы можно сделать вывод, что я не вникал глубоко в тонкости работы millis(), а прочитал первое что выдал поисковик.
оч интересно.
подпишусь.
где видео(оборудование скважины кроме ваших вычислительных мощностей тоже интересует), где графики? Всё ж для них было.
Решение с роутером весьма свежо. Я бы GPRS модем поставил.
1. Есть рабочая железка (роутер), которая нужна и помимо этого проекта - зачем мучаться, изобретая велосипед.
2. Точно такой же контроллер (веб-сервер будет висеть на другом порту) по весне будет стоять в теплице - родителям хочется видеть температуру в теплице, а летом открывать-закрывать форточки дистанционно.Потом еще что-нибудь будет - ставить на каждый контроллер свой модем с симкой - не имеет смысла.
Оборудование скважины - тема не этого форума, но там все просто - датчик сухого хода (который отключен, т.к. высушить скважину просто нереально, но после отключения эл-ва надо идти и нажимать на нем кнопку) и реле давления, накопительный бак стоит в другом месте. Потом добавлю какой-нибудь водосчетчик с импульсным выходом и электромагнитный клапан с дист.управлением.
виджеты от Thingspeak:
График, который рисуется по нажатию на виджет:
Так выглядит веб-морда контроллера: