Термостат для скважины с WiFi и thingspeak v2.0 (ESP-07 + DS18B20 4шт. + реле)

ki314
Offline
Зарегистрирован: 03.03.2017

Данный контроллер создавался для автоматического управления обогревом домика с насосным оборудованием скважины с возможностью удаленного ручного управления и удаленного контроля за температурой .
Оборудование скважины расположено не в колодце, а в утепленном домике на поверхности земли. На зимовку уходит второй раз и мне хочеться понять, насколько хорошо утеплен сам домик и до какой температуры хватит тепла из самой скважины, чтобы  поддерживать плюсовую температуру в домике, который утеплен 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);
  }
}

 

ki314
Offline
Зарегистрирован: 03.03.2017

Выявил глюк в Opere для Android - при нажатии на ссылку включения/выключения реле, как положено вы попадает на страницу, которая сообщает, что реле включено/выключено, но на самом деле ничего не происходит. Нужно обновить страницу, где будет написано "Выключил" ("Выключено"), тогда реле сработает.

Связываю это с кешированием данных в браузере, т.к. когда включаешь/выключаешь реле в первый раз - все отрабатывает нормально.

Добавление на страницу тега, который говорит браузеру он том, что кешировать не надо - не помогло.

Из Firefox работает нормально.

 

Баги, изначально присутствующие в коде:

При вычислении времени работы через millis() происходит накопление ошибки, но оно мало и для тех задач, которые выполняет контроллер, это не несет никакого вреда.

Плюс примерно через 50 дней работы без перезагрузки из-за переполнения этого millis() возможно перестанет работать опрос счетчиков :) но пока решил не бороться с этим, т.к. минимум раз в месяц контроллер будет перегружаться по питанию.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Я поставил вам  плюс, но, извините, слушать про переполнение millis'a  уже надоело.....

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

ki314 пишет:

Плюс примерно через 50 дней работы без перезагрузки из-за переполнения этого millis() возможно перестанет работать опрос счетчиков :) но пока решил не бороться с этим, т.к. минимум раз в месяц контроллер будет перегружаться по питанию.

Проблемы переполнения millis не существует. Достаточно правильно высчитывать интервалы. Обсасывалось сто раз на форуме, просто повторюсь вкратце. Вот так - неправильно:

uint16_t interval = 20000;
uint32_t past = millis();

.....

if(past + interval > millis()) {}

А вот так - правильно:

uint16_t interval = 20000;
uint32_t past = millis();

.....

if(millis() - past > interval) {}

И никакой проблемы.

ki314
Offline
Зарегистрирован: 03.03.2017

trembo пишет:

Я поставил вам  плюс, но, извините, слушать про переполнение millis'a  уже надоело.....

Ну за что купил, за то продал, прошу прощенья, если его нет, то отлично...

b707
Offline
Зарегистрирован: 26.05.2017

ki314 пишет:

Ну за что купил, за то продал, прошу прощенья, если его нет, то отлично...

из этой фразы можно сделать вывод, что скетч не ваш, а найден в инете. Если это так - это надо написать и указать ссылку, откуда брали.

ki314
Offline
Зарегистрирован: 03.03.2017

b707 пишет:

ki314 пишет:

Ну за что купил, за то продал, прошу прощенья, если его нет, то отлично...

из этой фразы можно сделать вывод, что скетч не ваш, а найден в инете. Если это так - это надо написать и указать ссылку, откуда брали.

из этой фразы можно сделать вывод, что я не вникал глубоко в тонкости работы millis(), а прочитал первое что выдал поисковик.

 

dmitron1036
Offline
Зарегистрирован: 10.01.2016

оч интересно.

подпишусь.

где видео(оборудование скважины кроме ваших вычислительных мощностей тоже интересует), где графики? Всё ж для них было.

Решение с роутером весьма свежо. Я бы GPRS модем поставил.

ki314
Offline
Зарегистрирован: 03.03.2017

dmitron1036 пишет:
Решение с роутером весьма свежо. Я бы GPRS модем поставил.

1. Есть рабочая железка (роутер), которая нужна и помимо этого проекта - зачем мучаться, изобретая велосипед.
2. Точно такой же контроллер (веб-сервер будет висеть на другом порту) по весне будет стоять в теплице - родителям хочется видеть температуру в теплице, а летом открывать-закрывать форточки дистанционно.Потом еще что-нибудь будет - ставить на каждый контроллер свой модем с симкой - не имеет смысла.

Оборудование скважины - тема не этого форума, но там все просто - датчик сухого хода (который отключен, т.к. высушить скважину просто нереально, но после отключения эл-ва надо идти и нажимать на нем кнопку) и реле давления, накопительный бак стоит в другом месте. Потом добавлю какой-нибудь водосчетчик с импульсным выходом и электромагнитный клапан с дист.управлением.

виджеты от Thingspeak:

График, который рисуется по нажатию на виджет:

Так выглядит веб-морда контроллера: