Web-сервер на arduino с контроллером температуры.

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

Andy пишет:

Как вариант, можно вообще всю страничку разместить на внешнем ресурсе, а в ардуинке только значения параметров.

Ага, если бы всё было так просто ;) Вы попробуйте, и расскажите нам о результатах, когда с одного домена, на котором у вас размещена страница, вы будете получать данные, отправляя запрос к другому домену (которым будет выступать дуина). И тогда сразу лихорадочно будем вспоминать про политики ограничения кроссдоменных запросов в браузерах, и как это побороть.

armlive
Offline
Зарегистрирован: 07.12.2015

Зачем вам два домена можете пояснить????
Ардуино справляется с этим прекрасно, будущий клиентом

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

DIYMan пишет:
Ага, если бы всё было так просто ;) Вы попробуйте, и расскажите нам о результатах, когда с одного домена, на котором у вас размещена страница, вы будете получать данные, отправляя запрос к другому домену (которым будет выступать дуина). И тогда сразу лихорадочно будем вспоминать про политики ограничения кроссдоменных запросов в браузерах, и как это побороть.

Рассказываю.

Данные передаем через iframe.src=http://ТвойДомен/arduina.html?param=value

Получаем через:

<script type="text/javascript" src="http://ТвойДомен/script.js"></script>

путем обновления страницы.

Начинай лихорадочно вспоминать про кроссдоменные запросы.

Deuce
Offline
Зарегистрирован: 20.08.2013

DIYMan

Прошу понять меня правильно и не обижаться. Есть код (выкладывал его ранее), который я подогнал на работу с восемью датчиками и восемью реле (подогнал из первоначального скетча в этой теме). Есть необходимость уйти с ENC28J60 на W5100. На то свои причины, главная из которой - неработоспособность ENC при превышении HTTP_части кода 1279 символов, включая пробелы. Сам я не программист ни разу, о чем тоже сообщал. И теперь, вместо того, что бы написать: "парни, пишите так и вот так", я вижу много слов на тему "код, что привели - в помойку", "есть RFC на тему...." Вопрос чисто риторический: вот какой практический смысл всего этого?  Блеснуть познаниями в RFC? Тогда где номер RFC? Просто блеснуть чем-либо? Ну ок, потешил эго, дальше что? Еще раз призываю понять меня правильно и не обижаться.

 

 

Andy
Andy аватар
Offline
Зарегистрирован: 01.01.2016

Deuce, в твоем коде никто не захочет ковыряться.

Вот здесь реализовано на W5100: http://arduino.ru/forum/programmirovanie/webserver-na-arduino-2560-i-eth...

Deuce
Offline
Зарегистрирован: 20.08.2013

Andy пишет:

Deuce, в твоем коде никто не захочет ковыряться.

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

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

armlive пишет:
Зачем вам два домена можете пояснить???? Ардуино справляется с этим прекрасно, будущий клиентом

Речь шла о локальной HTML-странице, с которой идёт запрос к ардуине. В терминах веб, локальная страница - тоже отдельный домен. Дальше объяснять или сами про CORS в вики почитаете?

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

Andy пишет:

DIYMan пишет:
Ага, если бы всё было так просто ;) Вы попробуйте, и расскажите нам о результатах, когда с одного домена, на котором у вас размещена страница, вы будете получать данные, отправляя запрос к другому домену (которым будет выступать дуина). И тогда сразу лихорадочно будем вспоминать про политики ограничения кроссдоменных запросов в браузерах, и как это побороть.

Рассказываю.

Данные передаем через iframe.src=http://ТвойДомен/arduina.html?param=value

Получаем через:

<script type="text/javascript" src="http://ТвойДомен/script.js"></script>

путем обновления страницы.

Начинай лихорадочно вспоминать про кроссдоменные запросы.

Дык то, что вы привели - костыль, который специально используют, чтобы обойти CORS, называется этот костыль - JSONP. Без P - работать не будет. Что такое P после JSON - предлагаю догадаться.

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

Deuce пишет:

DIYMan

Прошу понять меня правильно и не обижаться. Есть код (выкладывал его ранее), который я подогнал на работу с восемью датчиками и восемью реле (подогнал из первоначального скетча в этой теме). Есть необходимость уйти с ENC28J60 на W5100. На то свои причины, главная из которой - неработоспособность ENC при превышении HTTP_части кода 1279 символов, включая пробелы. Сам я не программист ни разу, о чем тоже сообщал. И теперь, вместо того, что бы написать: "парни, пишите так и вот так", я вижу много слов на тему "код, что привели - в помойку", "есть RFC на тему...." Вопрос чисто риторический: вот какой практический смысл всего этого?  Блеснуть познаниями в RFC? Тогда где номер RFC? Просто блеснуть чем-либо? Ну ок, потешил эго, дальше что? Еще раз призываю понять меня правильно и не обижаться.

Да никаких обид. Просто я речь веду о том, что если вы взялись делать что-то подобной сложности - надо для начала почитать хотя бы основы, а не кидаться сразу в бой. Ничем я блеснуть не хочу, мне и без этого работ и забот хватает. Я лишь предупреждаю, что при таком подходе - дальше будет только хуже, и понимания - не придёт ни на грош, будет только тупое и бессмысленное дёрганье кусков кода из чужих скетчей. Хочешь сделать хорошо? Сделай это сам!

У нас вообще классно всё устроено - можно без прав и знания ПДД шмалять по дорогам на убитых машинах - а чо, вон друзья ездят! Вот ваш подход примерно из той же оперы - не зная самых необходимых основ - пытаетесь сразу впихнуть невпихуемое. А чревато это, как и в случае с авто - скорой аварией. Впрочем, зато механикам работа всегда будет.

Короче, я на вас молюсь - не будь вас, не было бы у меня работы :)

armlive
Offline
Зарегистрирован: 07.12.2015

Где там речь о локальной странице??? Парень предложил просто саму страницу разместить на "внешнем ресурсе", Не придав смысла. А имел ввиду скорее всего создать страницу, к которой будет обращаться Ардуино, но ни как на оборот.
Если все же, я ошибся, то тогда встречный вопрос , а зачем все так усложнять???

armlive
Offline
Зарегистрирован: 07.12.2015

Конечно лет 10 назад, я бы еще понял, экономия трафика, но сейчас...

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

armlive пишет:
Где там речь о локальной странице??? Парень предложил просто саму страницу разместить на "внешнем ресурсе", Не придав смысла. А имел ввиду скорее всего создать страницу, к которой будет обращаться Ардуино, но ни как на оборот. Если все же, я ошибся, то тогда встречный вопрос , а зачем все так усложнять???

Может и я не так понял - кто к кому будет обращаться, бывает. К вопросу "зачем всё так усложнять" - а зачем вообще знать основы? Ведь можно взять - и наговнякать. Это не к вам лично, это вообще - современный потреблядский подход: сначала сделать, а потом чесать репу - почему не работает. Потом задать кучу ненужных вопросов, которые можно было бы решить, почитав сразу документацию.

Ну ведь основа же - политики безопасности браузеров. И кстати - есть сценарии использования, при которых вы не сможете без плясок с бубном получить данные со страницы в веб, неважно, запросив их с ардуины ли или ещё откуда. Но я об этом тихо промолчу, ок? А то всё равно это никому неинтересно.

armlive
Offline
Зарегистрирован: 07.12.2015

Даже очень интересно, Вот сейчас сижу и калдую, динамическую переменную получаю с удаленный вебстр

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

armlive пишет:
Даже очень интересно, Вот сейчас сижу и калдую, динамическую переменную получаю с удаленный вебстр

Я вот колбасу в дальнем магазине тоже покупаю без проблем, и что? Без указания юзкейса ваши слова - ни о чём. Я тоже могу получить чего-нибудь с веб-страницы, но - далеко не с каждой, и далеко не любым способом. Если вам это подвластно - поздравляю, вы волшебник.

Впрочем, разговор ни о чём, поскольку тут никто даже не заморачивается чтением хоть какой-либо документации, как правило.

З.Ы. У вас, кстати, при получении значения переменной анализируется Expires и, хотя бы, 302 Content moved? Я уже не говорю о Last-Modified - а то вы там такого старого говна получите с сервера, что уверенности в достоверности полученных данных не останется ни на грош. Впрочем, это неправильный, профессиональный подход, правильные пацаны таким не заморачиваются.

Вы, наверное, тоже не паритесь? ;)

armlive
Offline
Зарегистрирован: 07.12.2015

Вы правы, Мне незачем этим париться))). Я конечно не гуру, но у меня страница получает данные каждую секунду, записывая все в базу. Изменения на странице, Так-же записываются в базу и выводятся в строчку.Ну а дальше генерации графиков и т.п. Смысл создания был, Не привязываться к одному контролеру и максимальную гибкость. На данный момент можно добавлять очень много контроллеров, к примеру esp12 встроенных в выключатели, розетки и т.п. И управлять всем этим с одной страницы. Гибкость, пишу приложение под андроид.
Я к чему, что каждый начинает как может. Только хватит ума и терпения не каждому. Ну а кто пытается хоть как, молодцы, Все лучше чем х**ней страдать

uservasil
Offline
Зарегистрирован: 09.07.2015

Это называется поумничали и разошлись =), а по делу хоть номер RFC дайте, а еще лучше если уж Вы на нас молитесь и для Вас это просто напишите реализацию этих умных слов: "FORM описать атрибуты TARGET и METHOD, чтобы браузер знал, куда и каким методом засунуть данные, введённые пользователем. Затем - при отправке формы по кнопке <input type="submit"...> на URI, указанный в TARGET, методом, указанном в METHOD, придут данные, закодированные в соответствии с указанием кодировки, это описывается аттрибутом ENCTYPE формы. Принимаете HTTP-запрос для этого URI, разбираете переданные значения, декодируете их, если потребуется"

ЗЫ. память пока не жмет, ее еще много =)

uservasil
Offline
Зарегистрирован: 09.07.2015

Deuce пишет:

Andy пишет:

Deuce, в твоем коде никто не захочет ковыряться.

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

а может попробуем взять данные не из формы ввода, а из кнопки и например сделаем 2-4 кнопки к которым будет привяза определенная температура, быть может так будет проще?

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

uservasil пишет:

Это называется поумничали и разошлись =), а по делу хоть номер RFC дайте, а еще лучше если уж Вы на нас молитесь и для Вас это просто напишите реализацию этих умных слов: "FORM описать атрибуты TARGET и METHOD, чтобы браузер знал, куда и каким методом засунуть данные, введённые пользователем. Затем - при отправке формы по кнопке <input type="submit"...> на URI, указанный в TARGET, методом, указанном в METHOD, придут данные, закодированные в соответствии с указанием кодировки, это описывается аттрибутом ENCTYPE формы. Принимаете HTTP-запрос для этого URI, разбираете переданные значения, декодируете их, если потребуется"

ЗЫ. память пока не жмет, ее еще много =)

За вас писать? Зачем? У меня и так работы хватает. Вам нужен RFC по HTTP? В гугле не забанили часом? Так и набираете - HTTP RFC. Заодно можно стандарт HTML почитать, про те же атрибуты тега FORM, чтобы прояснение наступило.

По поводу "поумничали": не далее как пару дней назад ещё раз убедился, что политика безопасности браузеров работает, и, если не знать про CORS - дулю можно получить вместо результата запроса. Суть была проста: есть страничка, которая выдаётся Мегой с SD-карточки в браузер, через вай-фай-вундерфафлю ESP8266. С неё отправляю AJAX-запросы на мегу же, получаю ответ, чего-то там на страничке делаю JavaScript'ом. Ну ведь неудобно же - изменил страницу, для тестирования её надо закачать на карточку, а для этого карточку надо вынуть из меги. Сохранил страницу локально, открываю её, жмакаю кнопку "опросить датчики", а Firefox дулей крутит и говорит: мил человек, не покажу я тебе ответ от меги, т.к. не нашёл я в ответе заголовок один секретный, поэтому - кури в сторонке.

Думаю - а ведь точно, прошляпил по старости лет. Открыл прошивку, дописал выдачу заголовка Access-Control-Allow-Origin: * - и пошёл пить чай, т.к. всё заработало.

Но это реальным пацанам неинтересно - RFC ведь мало того, что читать надо, так они, цуки, ещё и на басурманском! Мы лучше так налабаем, а потом будем кричать - ааа, почему не работает мой кривой скетч?

З.Ы. Полная реализация обработки данных с формы, если делать в соответствии с RFC - вещь далеко не тривиальная и, боюсь, ресурсиков дурины тут не хватит.

З.З.Ы. Вот моё видео, в котором показана работа с браузером. Там и ссылка на проект на гитхабе есть.

http://www.youtube.com/watch?v=L8ox4UzQNxs

uservasil
Offline
Зарегистрирован: 09.07.2015

вот то что получилось у меня, создал новую тему потому что пришедший сюда новичек вроде меня, не найдет http://arduino.ru/forum/proekty/web-server-dlya-mega5100dhtds18b20

kefir
Offline
Зарегистрирован: 10.03.2015

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

BlackSnow
Offline
Зарегистрирован: 16.08.2015

Рад что кому-то пригодились мои труды =)

 Скоро выложу обновленный код на ESP

 

 

BlackSnow
Offline
Зарегистрирован: 16.08.2015
#include <Wire.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <WiFiUdp.h>
#include <pgmspace.h>
#include <RtcDS3231.h>
#include <OneWire.h>
#include <EEPROM.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
#include <DallasTemperature.h>

#define countof(a) (sizeof(a) / sizeof(a[0]))

char* const ver = "ESP-HomeMaster-0.60";

unsigned int localPort = 2390;

RtcDS3231<TwoWire> Rtc(Wire);

IPAddress timeServerIP;
const char* ntpServerName = "time.google.com";
//IPAddress timeServer(129, 6, 15, 28);
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
WiFiUDP Udp;

const byte PinTemp = 0;               // Датчики Dallas DS18B20 Temperature Sensors

int addr = 8; // i2c arduino

OneWire  ds(PinTemp);
DallasTemperature DS18B20(&ds);

int numberOfDevices; // Number of temperature devices found

DeviceAddress tempDeviceAddress;

float temp1;
float temp2;
float temp3;
float temp4;
byte CRC;

char chartemp1[10];
char chartemp2[10];
char chartemp3[10];
char chartemp4[10];

byte TargetTemp1;
byte TargetTemp2;
byte TargetTemp3;
byte TargetTemp4;

byte hs, ms, he, me;
byte hour, minute;

byte SSR1on = 0;  // 0 is Off, 1 is On
byte SSR2on = 0;  // 0 is Off, 1 is On
byte SSR3on = 0;  // 0 is Off, 1 is On
byte SSR4on = 0;  // 0 is Off, 1 is On

byte sensor1[8];
byte sensor2[8];
byte sensor3[8];
byte sensor4[8];


char datestring[20];

const char* ssid = "-------------";
const char* password = "-------------";
unsigned long previousMillis = 0;
unsigned long UpdateMillis = 0;

ESP8266WebServer server ( 80 );

void checkupd() {
  unsigned long currentMillis = millis();
  if ((unsigned long)(currentMillis - UpdateMillis) >= 60000) {
    UpdateMillis = currentMillis;
    t_httpUpdate_return ret = ESPhttpUpdate.update("http://------------/esp/update.php", ver);

    switch (ret) {
      case HTTP_UPDATE_FAILED:
        break;

      case HTTP_UPDATE_NO_UPDATES:
        break;

      case HTTP_UPDATE_OK:
        break;
    }
  }
}

float tempread(byte sensoraddr[])
// error codes:
// -1 no sensors found
// -2 invalid CRC
// -3 not a DS1820
{
  byte i;
  byte present = 0;
  byte data[12];
  byte addr[8];

  for ( i = 0; i < 8; i++) {
    addr[i] = sensoraddr[i];
  }
  ds.reset(); // pulse the pins and wait for a response to reset the DS1820
  ds.select(addr); // 0x55 (MATCH_ROM) followed by the address of the 1820 to talk to.

  ds.write(0x44, 0); //PARASITE POWER OFF

  present = ds.reset();
  ds.select(addr);
  ds.write(0xBE);    // Read Scratchpad

  for ( i = 0; i < 9; i++) {       // we need 9 bytes
    data[i] = ds.read();
  }
  int16_t raw = (data[1] << 8) | data[0];
  if (raw == -1 or raw == 0) {
    if (CRC < 250) CRC++;
    raw = 4000;
  }
  else
  {
    CRC = 0;
  }
  float temp = (float)raw / 16.0;
  return temp;
}

void tim(const RtcDateTime& dt)
{
  hour = dt.Hour();
  minute = dt.Minute();
}

void relays() {
  RtcDateTime now = Rtc.GetDateTime();
  tim(now);

  if (temp1 < TargetTemp1 - 1 and SSR1on == 0 and (CRC > 10 or CRC == 0)) {
    byte senderArray[] = {4, 1, 0};
    int addr = 8;
    Wire.beginTransmission(addr); // transmit to device #8
    Wire.write(senderArray, 3); // sends
    Wire.endTransmission();
    SSR1on = 1;
  }
  else if (temp1 > TargetTemp1 + 1 and SSR1on == 1 and (CRC > 10 or CRC == 0)) {
    byte senderArray[] = {4, 1, 200};
    int addr = 8;
    Wire.beginTransmission(addr); // transmit to device #8
    Wire.write(senderArray, 3); // sends
    Wire.endTransmission();
    SSR1on = 0;
  }

  if (temp2 < TargetTemp2 - 1 and SSR2on == 0 and (CRC > 10 or CRC == 0)) {
    byte senderArray[] = {5, 1, 0};
    int addr = 8;
    Wire.beginTransmission(addr); // transmit to device #8
    Wire.write(senderArray, 3); // sends
    Wire.endTransmission();
    SSR2on = 1;
  }
  else if (temp2 > TargetTemp2 + 1 and SSR2on == 1 and (CRC > 10 or CRC == 0)) {
    byte senderArray[] = {5, 1, 200};
    int addr = 8;
    Wire.beginTransmission(addr); // transmit to device #8
    Wire.write(senderArray, 3); // sends
    Wire.endTransmission();
    SSR2on = 0;
  }
  if (temp3 < TargetTemp3 - 1 and SSR3on == 0 and (CRC > 10 or CRC == 0)) {
    byte senderArray[] = {2, 1, 0};
    int addr = 8;
    Wire.beginTransmission(addr); // transmit to device #8
    Wire.write(senderArray, 3); // sends
    Wire.endTransmission();
    SSR3on = 1;
  }
  else if (temp3 > TargetTemp3 + 1 and SSR3on == 1 and (CRC > 10 or CRC == 0)) {
    byte senderArray[] = {2, 1, 200};
    int addr = 8;
    Wire.beginTransmission(addr); // transmit to device #8
    Wire.write(senderArray, 3); // sends
    Wire.endTransmission();
    SSR3on = 0;
  }

  if (temp4 < TargetTemp4 - 1 and SSR4on == 0 and (CRC > 10 or CRC == 0)) {
    byte senderArray[] = {3, 1, 200};
    int addr = 8;
    Wire.beginTransmission(addr); // transmit to device #8
    Wire.write(senderArray, 3); // sends
    Wire.endTransmission();
    SSR4on = 1;
  }
  else if (temp4 > TargetTemp4 + 1 and SSR4on == 1 and (CRC > 10 or CRC == 0)) {
    byte senderArray[] = {3, 1, 0};
    int addr = 8;
    Wire.beginTransmission(addr); // transmit to device #8
    Wire.write(senderArray, 3); // sends
    Wire.endTransmission();
    SSR4on = 0;
  }
}

void Pdt(const RtcDateTime& dt)
{
  snprintf(datestring,
           20,
           "%02d.%02d.%04d %02d:%02d:%02d",
           dt.Day(),
           dt.Month(),
           dt.Year(),
           dt.Hour(),
           dt.Minute(),
           dt.Second() );
}

void tempupd() {
  temp1 = tempread(sensor1);
  dtostrf(temp1, 4, 2, chartemp1);
  temp2 = tempread(sensor2);
  dtostrf(temp2, 4, 2, chartemp2);
  temp3 = tempread(sensor3);
  dtostrf(temp3, 4, 2, chartemp3);
  temp4 = tempread(sensor4);
  dtostrf(temp4, 4, 2, chartemp4);
}

void ds18b20() {
  if (server.hasArg("ttemp1")) {
    int SensorId = server.arg("ttemp1").toInt();
    DS18B20.getAddress(tempDeviceAddress, SensorId);
    for (uint8_t k = 0; k < 8; k++)
    {
      int p = k + 10;
      EEPROM.write(p, tempDeviceAddress[k]);
      sensor1[k] = tempDeviceAddress[k];
    }
    EEPROM.commit();
  }
  if (server.hasArg("ttemp2")) {
    int SensorId = server.arg("ttemp2").toInt();
    DS18B20.getAddress(tempDeviceAddress, SensorId);
    for (uint8_t k = 0; k < 8; k++)
    {
      int p = k + 20;
      EEPROM.write(p, tempDeviceAddress[k]);
      sensor2[k] = tempDeviceAddress[k];
    }
    EEPROM.commit();
  }
  if (server.hasArg("ttemp3")) {
    int SensorId = server.arg("ttemp3").toInt();
    DS18B20.getAddress(tempDeviceAddress, SensorId);
    for (uint8_t k = 0; k < 8; k++)
    {
      int p = k + 30;
      EEPROM.write(p, tempDeviceAddress[k]);
      sensor3[k] = tempDeviceAddress[k];
    }
    EEPROM.commit();
  }
  if (server.hasArg("ttemp4")) {
    int SensorId = server.arg("ttemp4").toInt();
    DS18B20.getAddress(tempDeviceAddress, SensorId);
    for (uint8_t k = 0; k < 8; k++)
    {
      int p = k + 40;
      EEPROM.write(p, tempDeviceAddress[k]);
      sensor4[k] = tempDeviceAddress[k];
    }
    EEPROM.commit();
  }
  DS18B20.begin();
  numberOfDevices = DS18B20.getDeviceCount();
  DS18B20.requestTemperatures();
  String output = "<!DOCTYPE html> <html>\
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>\
<title>Умный дом - настройка датчиков</title>";
  output += "Devices: ";
  output += numberOfDevices;
  output += "<br>";
  for (int i = 0; i < numberOfDevices; i++)
  {
    if (DS18B20.getAddress(tempDeviceAddress, i))
    {
      output += "ID " + String(i) + " - ";
      for (uint8_t k = 0; k < 8; k++)
      {
        output += "0x";
        // zero pad the address if necessary
        if (tempDeviceAddress[k] < 16) output += "0";
        output += String(tempDeviceAddress[k], HEX);
        output += " ";
      }

      output += ": ";
      output += DS18B20.getTempCByIndex(i);
      //printAddress(tempDeviceAddress);
      output += "&deg;C<br>";
    }
  }
  output += "<br>Прихожая <form method=POST><input type=text name=ttemp1 size=1> <input type=submit value=ОК> </form><br>\
Ванна <form method=POST><input type=text name=ttemp2 size=1> <input type=submit value=ОК> </form><br>\
Кухня <form method=POST><input type=text name=ttemp3 size=1> <input type=submit value=ОК> </form><br>\
Зал <form method=POST><input type=text name=ttemp4 size=1> <input type=submit value=ОК> </form><br>";
  server.send ( 200, "text/html", output );
}

void root() {
  if (server.hasArg("ttemp1")) {
    TargetTemp1 = server.arg("ttemp1").toInt();
    EEPROM.write(1, TargetTemp1);
    EEPROM.commit();
  }
  if (server.hasArg("ttemp2")) {
    TargetTemp2 = server.arg("ttemp2").toInt();
    EEPROM.write(2, TargetTemp2);
    EEPROM.commit();
  }
  if (server.hasArg("ttemp3")) {
    TargetTemp3 = server.arg("ttemp3").toInt();
    EEPROM.write(3, TargetTemp3);
    EEPROM.commit();
  }
  if (server.hasArg("ttemp4")) {
    TargetTemp4 = server.arg("ttemp4").toInt();
    EEPROM.write(4, TargetTemp4);
    EEPROM.commit();
  }
  relays();

  char* ssr1;
  char* ssr2;
  char* ssr3;
  char* ssr4;
  int temp1i, temp1i2, temp2i, temp2i2, temp3i, temp3i2, temp4i, temp4i2;
  temp1i = temp1;
  temp2i = temp2;
  temp3i = temp3;
  temp4i = temp4;
  temp1i2 = trunc((temp1 - temp1i) * 100);
  temp2i2 = trunc((temp2 - temp2i) * 100);
  temp3i2 = trunc((temp3 - temp3i) * 100);
  temp4i2 = trunc((temp4 - temp4i) * 100);


  int sec = millis() / 1000;
  int min = sec / 60;
  int hr = min / 60;
  int days = hr / 24;
  if (SSR1on == 0) ssr1 = "<font color=red><b>ВЫКЛ</b>";
  else ssr1 = "<font color=green><b>ВКЛ</b>";
  if (SSR2on == 0) ssr2 = "<font color=red><b>ВЫКЛ</b>";
  else ssr2 = "<font color=green><b>ВКЛ</b>";
  if (SSR3on == 0) ssr3 = "<font color=red><b>ВЫКЛ</b>";
  else ssr3 = "<font color=green><b>ВКЛ</b>";
  if (SSR4on == 0) ssr4 = "<font color=red><b>ВЫКЛ</b>";
  else ssr4 = "<font color=green><b>ВКЛ</b>";
  RtcDateTime now = Rtc.GetDateTime();
  Pdt(now);
  char temp[2000];
  snprintf_P ( temp, 2000, "<!DOCTYPE html><html><head>\
<meta http-equiv='refresh' content='120'/>\
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>\
<title>Умный дом</title>\
<link rel='stylesheet' type='text/css' href='/main.css' media='all'>\
</head><body>\
<div class='main'>\
<center>\
<table border='0' class='catalogue' >\
<tr><td colspan=5 align=center><h3>Управление обогревом</h3></td></tr>\
<tr style='background-color: silver'>\
<td>Помещение:</td><td>Прихожая</td><td>Ванна</td><td>Кухня</td><td>Зал</td></tr>\
<tr><td>Температура:</td><td>%s &deg;C</td><td>%s &deg;C</td><td>%s &deg;C</td><td>%s &deg;C</td></tr>\
<tr><td>Обогрев:</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>\
<tr><td>Цель:</td><td>%d &deg;C</td><td>%d &deg;C</td><td>%d &deg;C</td><td>%d &deg;C</td></tr>\
<tr><td>Установить:</td>\
<td><form method=POST><input type=text name=ttemp1 size=1> <input type=submit value=ОК> </form></td>\
<td><form method=POST><input type=text name=ttemp2 size=1> <input type=submit value=ОК> </form></td>\
<td><form method=POST><input type=text name=ttemp3 size=1> <input type=submit value=ОК> </form></td>\
<td><form method=POST><input type=text name=ttemp4 size=1> <input type=submit value=ОК> </form></td>\
</tr></table><br>\
<table class='catalogue'>\
<tr><td colspan=2 align=center><h3>Часы</h3></td></tr>\
<tr style='background-color: silver'>\
<td>Аптайм</td><td>Время</td></tr>\
<tr><td>%dдн. %02d:%02d:%02d</td>\
<td>%s</td>\
</tr>\
</table>\
</center></div></body></html>", chartemp1, chartemp2, chartemp3, chartemp4, ssr1, ssr2, ssr3, ssr4, TargetTemp1, TargetTemp2, TargetTemp3, TargetTemp4, days, hr % 24, min % 60, sec % 60, datestring);
  server.send ( 200, "text/html", temp );
}

void css () {
  server.send ( 200, "text/css",  "body {  background:#990000;font-family:'Lucida Grande', Tahoma, Arial, Verdana, sans-serif;font-size:medium;margin:8px 0 16px;text-align:center; }\
.main { background:#fff;margin:0 auto;text-align:left;width:640px;box-shadow: 13px 6px 16px rgba(22,22,22,0.5);border-radius:6px;padding: 3px 4px 3px 4px; }\
TABLE.catalogue {BACKGROUND: #FEFEFE; margin: 2px 2px 2px 5px; BORDER: #999999 1px solid; WIDTH: 99%; BORDER-COLLAPSE: collapse; }\
TABLE.catalogue td {FONT-SIZE: 14px; text-align: center; padding:2px 2px 2px 2px; BORDER: #BBBBBB 1px solid;  }\
input { text-align: center; FONT-SIZE: 14px; }\
h3 {padding:0;margin:0;}");
}

void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for ( uint8_t i = 0; i < server.args(); i++ ) {
    message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
  }
  server.send ( 404, "text/plain", message );
}

unsigned long sendNTPpacket(IPAddress& address) {
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

void timeupd () {
  unsigned long currentMillis = millis();
  if ((unsigned long)(currentMillis - previousMillis) >= 36000000) {
    previousMillis = currentMillis;
    Serial.println("TimeUpdate!");

    WiFi.hostByName(ntpServerName, timeServerIP);

    sendNTPpacket(timeServerIP);
    delay(1000);

    int cb = Udp.parsePacket();
    if (cb) {
      Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
      unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
      unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
      unsigned long secsSince1900 = highWord << 16 | lowWord;
      Serial.print("Seconds since Jan 1 1900 = " );
      Serial.println(secsSince1900);
      Serial.print("From 2000 time = ");
      const unsigned long seventyYears = 3155662800UL;
      unsigned long epoch = secsSince1900 - seventyYears;
      Serial.println(epoch);
      Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
      Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
      Serial.print(':');
      if ( ((epoch % 3600) / 60) < 10 ) {
        Serial.print('0');
      }
      Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
      Serial.print(':');
      if ( (epoch % 60) < 10 ) {
        Serial.print('0');
      }
      Serial.println(epoch % 60); // print the second

      RtcDateTime compiled = RtcDateTime(epoch);
      if (!Rtc.IsDateTimeValid())
      {
        Rtc.SetDateTime(compiled);
      }
      if (!Rtc.GetIsRunning())
      {
        Rtc.SetIsRunning(true);
      }
      RtcDateTime now = Rtc.GetDateTime();
      Rtc.SetDateTime(compiled);
    }
  }
}

void setup() {

  EEPROM.begin(50);

  pinMode(PinTemp, INPUT);
  Udp.begin(localPort);

  TargetTemp1 = EEPROM.read(1);
  TargetTemp2 = EEPROM.read(2);
  TargetTemp3 = EEPROM.read(3);
  TargetTemp4 = EEPROM.read(4);

  for (int i = 0; i < 8; i++) {
    int k = i + 10;
    sensor1[i] = EEPROM.read(k);
    k = k + 10;
    sensor2[i] = EEPROM.read(k);
    k = k + 10;
    sensor3[i] = EEPROM.read(k);
    k = k + 10;
    sensor4[i] = EEPROM.read(k);
  }

  WiFi.mode(WIFI_STA);
  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() );

  server.on ( "/", root );
  server.on ( "/ds18b20", ds18b20 );
  server.on ( "/main.css", css );
  server.onNotFound ( handleNotFound );
  server.begin();

  Wire.begin(); // join i2c bus (address optional for master)
  Wire.pins(4, 5);

  int addr = 8;

  byte senderArray2[] = {2, 1, 200};
  Wire.beginTransmission(addr); // transmit to device #8
  Wire.write(senderArray2, 3); // sends
  Wire.endTransmission();

  byte senderArray3[] = {3, 1, 0};
  Wire.beginTransmission(addr); // transmit to device #8
  Wire.write(senderArray3, 3); // sends
  Wire.endTransmission();

  byte senderArray4[] = {4, 1, 200};
  Wire.beginTransmission(addr); // transmit to device #8
  Wire.write(senderArray4, 3); // sends
  Wire.endTransmission();

  byte senderArray5[] = {5, 1, 200};
  Wire.beginTransmission(addr); // transmit to device #8
  Wire.write(senderArray5, 3); // sends
  Wire.endTransmission();




  Serial.print("compiled: ");
  Serial.print(__DATE__);
  Serial.print(" ");
  Serial.println(__TIME__);
  Rtc.Begin();

  RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
  Pdt(compiled);
  Serial.println();

  if (!Rtc.IsDateTimeValid())
  {
    Serial.println("RTC lost confidence in the DateTime!");
    Rtc.SetDateTime(compiled);
  }

  if (!Rtc.GetIsRunning())
  {
    Serial.println("RTC was not actively running, starting now");
    Rtc.SetIsRunning(true);
  }

  RtcDateTime now = Rtc.GetDateTime();
  if (now < compiled)
  {
    Serial.println("RTC is older than compile time!  (Updating DateTime)");
    Rtc.SetDateTime(compiled);
  }
  else if (now > compiled)
  {
    Serial.println("RTC is newer than compile time. (this is expected)");
  }
  else if (now == compiled)
  {
    Serial.println("RTC is the same as compile time! (not expected but all is fine)");
  }

  // never assume the Rtc was last configured by you, so
  // just clear them to your needed state
  Rtc.Enable32kHzPin(false);
  Rtc.SetSquareWavePin(DS3231SquareWavePin_ModeNone);
  timeupd ();
}

void loop() {
  server.handleClient();
  timeupd();
  relays();
  tempupd();
  checkupd();
}

Небольшие пояснения по коду:

1) Это ESP8266.

2) Этот модуль работает на 3.3в, а модули реле у меня требуют 5в, поэтому пришлось использовать Arduino как прокладку =) ESP с Ардуино общается по Wire

3) Еще есть RTC модуль для хранения времени, тоже подключен по Wire.

4) Добавилась страница /ds18b20 где выводится список всех датчиков, которые можно сохранить в EEPROM встроенный в ESP, чтоб не прописывать в коде руками.

5) Есть OTA обновления по воздуху.