Главная » Хабрахабр » [Из песочницы] Полный цикл разработки IoT устройства управления подогревом бассейна на ESP8266 в среде Arduino

[Из песочницы] Полный цикл разработки IoT устройства управления подогревом бассейна на ESP8266 в среде Arduino

В данной публикации я поделюсь опытом о создании IoT устройства с ноля: от появления идеи и воплощении ее в «железе» до создания микропрограммы для контроллера и web-интерфейса для управления созданным устройством через сеть интернет.

До создания этого устройства я:

  • Почти не разбирался схемотехнике. Только на уровне принципов работы
    резистора/транзистора… Я не имел никакого опыта в создании сколь-нибудь сложных схем.
  • Никогда не проектировал печатных плат.
  • Никогда не паял SMD компонент. Уровень владения паяльником был на уровне припаивания проводов и какого-нибудь реле.
  • Никогда не писал таких сложных программ для микроконтроллера. Весь опыт был на уровне «зажги светодиод в Arduino», а контроллер ESP8266 я встретил впервые.
  • Совсем немного писал на C++ для «большого брата», но это было более десятка лет назад и все давно забылось.

NET) и системное мышление помогли мне разобраться в теме. Конечно, опыт работы программистом (главным образом это Microsoft . Полезных ссылок и статей в интернете море. Думаю, сможет и читатель этой публикации. Самые, на мой взгляд интересные, и помогающие разобраться в теме, я привожу по ходу статьи.

Постановка задачи

В нашем нестабильном климате оказалось, что в бассейне купаться некомфортно, если он стоит на открытом воздухе: вода выхолаживается ночью, а ветренная погода днем не делает купание комфортным. Я живу в частном доме под Минском, и собственный бассейн, пусть и простейший каркасный, является неотъемлемой частью того набора «бенефитов», который получают многие, живущие в загородном доме. В прошлом году я своими руками построил геодезический купол фуллера над бассейном, поставил горочку и повесил тарзанку – дети довольны.

Фотоотчет строительства купола на Flickr.

В этом году я пошел еще дальше и решил организовать подогрев бассейна от газового котла,
который служит для отопления дома в зимний период и подогрева горячей воды в летний.

Подогрев воды бассейна осуществляется с помощью титанового теплообменника, по первичному контуру которого проходит теплоноситель (горячая вода без примесей) из отопительного контура, а по вторичному – вода из бассейна, нагнетаемая насосом рециркуляции системы фильтрации. На лето «отопительный» контур котла с помощью вентилей переключается на подогрев
бассейна. Нельзя просто так взять и пустить воду напрямую через котел – иначе все трубы разъест солью. Поскольку бассейн я использую с хлоратором (много интересного по теме расписано на ForumHouse), в воде содержится немного соли и теплообменник нужен титановый.

Сам теплоноситель при этом остывает на пару десятков градусов, и возвращается в котел с тем, чтобы быть снова
подогретым. Проходя через теплообменник, теплоноситель, нагретый котлом, с температурой около 70-90 °C отдает тепло воде из бассейна, нагревая ее на пару градусов. Соотношение остывания воды от котла с нагревом воды бассейна зависит от многих факторов: мощности теплообменника и скорости циркуляции воды в первичном и вторичных контурах.

Дешевизна, способность выдержать приличное давление, отсутствие коррозии – вот основные достоинства таких труб. Трубы, подведенные от бассейна к теплообменнику – обычные полиэтиленовые, те, которые
в настоящее время применяются для подвода холодной воды в частные дома. В принципе, для бассейна этого более чем достаточно. Для всех без исключения полиэтиленовых труб рабочая температура ограничена 40 градусами по шкале Цельсия.

Однако, существует высокая вероятность развития аварийной ситуации в случае, если насос
рециркуляции воды бассейна воды остановится по какой-либо причине, а котел будет продолжать греть теплообменник: в этом случае вода во вторичном контуре теплообменника достаточно быстро повысится до температуры первичного контура, а значит, примыкающие к теплообменнику участки полиэтиленовых труб расплавятся, а вода из бассейна затопит все пространство вокруг.

Необходимо предусмотреть возможность защиты перегрева теплообменника.

Быстрое решение

Кроме того, датчики температуры, размещенные на вторичном контуре
теплообменника, обеспечивают второй эшелон обороны, отслеживая возможный перегрев. Для решения этой задачи в цепь контура рециркуляции воды бассейна был включен датчик потока, работающий на принципе эффекта холла.

котел все еще по инерции гоняет подогретую воду по контуру, не допуская перегрева «себя, любимого». Контролировать перегрев только по датчикам температуры невозможно: система обладает большой инерционностью: после внезапного останова воды в контуре бассейна, при
отключении котла, температура еще продолжает какое-то время расти, т.к.

Поэтому важно среагировать как можно раньше: а именно по останову потока воды в контуре
бассейна.

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

Датчики температуры было решено использовать Dallas DS18B20, их легко подключить сразу несколько штук на одной шине 1-Wire.

Дополнительным преимуществом такого подхода является
возможность мониторинга параметров системы: можно отслеживать насколько охлаждается теплоноситель в первичном контуре и насколько нагревается вода из бассейна во вторичном контуре. Решено было повесить по паре датчиков на вход и выход как вторичного, так и первичного
контура: всего 4 датчика. А значит – отслеживать оптимальность нагрева и прогнозировать время включения нагрева.

Места установки датчиков на теплообменнике и подводящих трубах

Параметры устройства

Первый прототип устройства был собран на базе Arduino Uno, и успешно запущен.

Подогрев 16 кубометров воды даже всего на
несколько градусов – дело не быстрое. Но тут же выяснилось, что хотелось бы большего. А заодно интересно было бы снимать графики нагрева, например за сутки. И хотелось бы прямо с работы отслеживать параметры нагрева, включать/выключать его.

Ну и раз мы уже получаем IoT устройство, то почему бы нам не контролировать заодно удаленное включение хлоратора бассейна и насоса для него?

Техзадание

Он должен уметь: Итак, было решено разработать устройство – многофункциональный контроллер бассейна.

  • Управлять подогревом бассейна через теплообменник, включая/выключая газовый котел для нагрева воды.
  • Не допускать перегрева теплообменника, контролируя наличие потока воды бассейна во вторичном контуре и превышения температуры вторичного контура.
  • Отображать статистику нагрева в реальном времени (температура на входе и выходе обоих контуров).
  • Записывать (логгировать) значения температур во флеш-память. Отображать данные за
    определенный период в виде графика.
  • С помощью реле уметь включать/выключать насосы бассейна и хлоратор.
  • Управлять всеми параметрами устройства дистанционно, через встроенный микро-веб сервер.

Но от этих «наворотов» на первом этапе
было решено отказаться. Было еще искушение прикрутить Blink, MQTT. Встроенного веб сервера для моих целей вполне достаточно. И тем более, не хотелось бы выносить возможность управления куда-то наружу. А безопасность обеспечивается тем, что в домашнюю сеть из внешнего мира можно войти только через VPN.

Аппаратная часть

Он отлично подходил для моих целей, кроме одного момента: согласования уровней сигналов 5-вольтовых датчиков с 3. В качестве контроллера было решено использовать дешевый и популярный ESP8266. В принципе, датчики Dallas вроде бы работают и на 3 вольтах, но у меня от контроллера до датчиков достаточно длинная линия, около 7 метров. 3 вольтовой логикой контроллера. Поэтому лучше напряжение повыше.

Было определено, что необходимо иметь по «железу»:

  • Контроллер ESP8266 или его старший брат ESP32 (в виде модуля DevKit).
  • Согласование уровней сигналов для датчиков.
  • Регулятор питания 5-вольтовой частью схемы.
  • Модуль управления реле.
  • Часы RTC + флеш память для записи логов.
  • Простейший 2-строчный LCD дисплей для отображения текущих значений датчиков и состояния прибора и реле.
  • Несколько физических кнопок, для управления состоянием устройства без доступа через web.

3в. Многие компоненты из списка продаются в виде модулей для Arduino и многие модули совместимы с логикой 3. Да и за деньги, отданные китайцам за модули можно вполне нарисовать и заказать свою индивидуальную печатную плату, а ожидание её приезда будет компенсировано сравнительно быстрым и надежным монтажом. Однако «слепливать» все это на макетной плате пучками проводов не хотелось, ведь хочется иметь аккуратный красивый «девайс».

Пришлось много учиться. Еще раз замечу, что это мой первый опыт в схемотехнике и в конструировании аппаратной части подобных вещей. Но делать все «на коленках» не позволил живущий во мне дух перфекционизма. Ведь по специальности я немного в стороне от микроконтроллеров.

Принципиальная схема

Мне, без опыта работы в этой области, сходу понравился EasyEDA – бесплатный онлайн редактор, позволяющий красиво разрисовать принципиальную схему, проверить, что ничего не было забыто и все компоненты имеют соединения, нарисовать печатную плату, а потом сразу же и заказать ее изготовление. На рынке существует большое количество программ, позволяющих нарисовать схему и печатную плату.

Решено было разрисовать схему так, чтобы можно было поставить DevKit любой ширины и с любым расположением выводов, а по сторонам от него — по 2 ряда парных отверстий-перемычек, и впоследствии проводками соединить нужные выводы, применительно к конкретно купленному контроллеру. Первая же сложность с которой я столкнулся: вариантов DevKit контроллера ESP8266 или ESP32 существует достаточно много, некоторые из них отличаются расположением выводов и их назначением, а некоторые даже по ширине.

Место под контроллер и 2 ряда парных перемычек: JH1 и JH2 на схеме:

3v питания встроенного стабилизатора, а также GND мне показались однотипными у разных DevKit, но все же я решил перестраховаться и тоже сделать их перемычками: JP1, JP2, JP3 на схеме. Расположение пинов входа 5v и выхода 3.

Перемычки со стороны подсоединения их к компонентам схемы, я решил подписать функциями, которые они вероятно будут выполнять.

А вот как это выглядит вместе с DevKit ESP8266, который я в итоге купил и поставил

Здесь D1 (GPIO5) и D2 (GPIO4) отвечают за I2C шину, D5 (GPIO14) за 1-Wire, D6 (GPIO12) – за получение импульсов с датчика потока.

Принципиальная схема:


(изображение кликабельно)

3в, нам еще нужно иметь 5 вольт для питания датчиков и LCD, и 12 вольт для питания реле. Несмотря на наличие на борту ESP8266 встроенного стабилизатора питания на 3. 0, дающий на выходе нужные 5 вольт. Решено было сделать питание платы 12 вольтовым, а на вход поставить регулятор напряжения AMS1117-5.

Для согласования уровней сигналов по шине 1-Wire я использовал полевой транзистор BSS138 c с «подтяжками» по напряжению с обеих сторон.

3В устройств. Очень хорошо про согласование уровней написано в статье Согласование логических уровней 5В и 3.

Датчик потока – это просто устройство с открытым коллектором. Для согласования уровней сигнала датчика потока я использовал просто делитель напряжения на резисторах. В некоторых датчиках уже может быть встроенный резистор для подтяжки, это нужно учитывать:

Справа от коннектора – подобранные мною делители напряжения с тем, чтобы иметь на выходе максимальный уровень в 3. Синим на схеме – схематическое обозначения датчика потока в сборе. 3 вольта.

Встроенная в ESP8266 флеш-память не годится, потому что обладает малым количеством циклов перезаписи (10 тыс. На I2C шину я повесил часы реального времени DS3231SN и флеш-память AT24C256C для хранения логов. против 1 миллиона у AT24Cxxx, если верить даташитам).

Управление реле организовано на связке чипов PCF8574AT и ULN2803A.

Состояние активного выхода или входа PCF8574AT выбирается выбором адреса по шине I2C.
Чип имеет некоторые интересные особенности, хорошо описанные в статье I2C расширитель портов PCF8574. Первый чип представляет собой I2C расширитель портов микроконтроллера.

Для этого используется транзисторная матрица ULN2803A. Напрямую нагрузкой (реле) управлять чип не может. К сожалению, при таком включении мы получаем побочный эффект: значение сигнала от контроллера инвертируется, и все реле «перещелкиваются» при включении схемы. Есть особенность: матрица легко может притянуть свои выходы с нагрузкой к земле, а это значит, что, если на второй полюс реле будет подано напряжение питания, по обмотке реле потечет ток и контакты реле замкнутся. Я пока не придумал, как убрать эту особенность.

Подробнее про чип описано здесь.

На схеме пины 4-7 могут быть использованы для чтения состояния кнопок. Расширитель портов PCF8574AT так же может использоваться на вход: на часть входов к нему можно повесить аппаратные кнопки, считывая их значения по шине I2C. Главное не забыть программно включить встроенную подтяжку соответствующих ног к питанию.

Для возможных подключений все выводы я завел на коннекторы (точнее на отверстия под них, куда могут быть подпаяны провода или впаян стандартный 2. В то же время я оставил разводку и на транзисторную матрицу, на случай если вдруг захочется подключить дополнительные реле. 54 мм DIP разъем).

Его можно подсоединить к свободному порту на контроллере и установить сработку прерывания по изменению состояния этого пина. Пин INT расширителя портов может быть использован для быстрой реакции на нажатие кнопки.

Основной момент: питание дисплея осуществляется от 5 вольт, в то время как сам дисплей управляется 3-вольтовой логикой. Двухстрочный LCD дисплей так же управляется через расширитель PCF8574AT. Идею такого подключения я нашел где-то на просторах интернет, к сожалению, ссылку я потерял, поэтому не привожу первоисточник. К слову, стандартные Arduino-адаптеры для I2C не рассчитаны на двойное напряжение.

Печатная плата

Почитав в интернете, что SMD монтаж не так уж сложен, а при должной сноровке даже менее трудоемок, я решил проектировать плату под SMD детали. При проектировании платы выяснилось, что обычные детали с ножками занимают слишком много места, а многие чипы в DIP исполнении непросто найти. Получилась компактная, красивая плата, куда я с легкостью разместил все, что мне нужно. И не ошибся. SMD детали, при наличии хорошего паяльника, флюса и припоя, оказалось действительно очень несложно монтировать.

На плату я добавил несколько квадратных полей отверстий для макетирования, если вдруг захочется допаять что-нибудь еще.

Она с легкостью помещается в стандартную разделочную электро-коробку. Печатную плату я сделал размером 97x97 мм. Изготовление минимальной партии из 5 плат по разработанному макету обошлось в 5 USD, еще около 9 USD стоила их доставка в Беларусь. Кроме того, платы размерами менее 100х100 дешевы в изготовлении.

Проект платы лежит на сайте EasyEDA и доступен каждому желающему.

Здесь же и на EasyEDA я выложил «почищенный» от всех этих ненужностей вариант. Замечу, что на фото контроллера ниже фигурирует первый образец платы, на который я «наворотил» еще много чего лишнего и ненужного (в надежде использовать эту минимальную партию из 5 плат и в других проектах).

Фотографии обеих сторон платы

Лицевая сторона:

Тыльная сторона:

Программная часть

Для программирования микроконтроллера, учитывая задел в виде прототипа на Arduino Uno, было решено использовать среду Arduino с установленной ESP8266 Arduino Core. Да, на ESP8266 можно использовать Lua, но, говорят, бывают подвисания. Мне же, учитывая критически важную выполняемую функцию, совсем бы этого не хотелось.
Сама среда Arduino мне кажется немного морально устаревшей, но, к счастью, есть расширение для Visual Studio от Visual Micro. Среда позволяет использовать подсказки IntelliSence по коду, быстро переходить к объявлениям функций, рефакторить код: в общем все то, что позволяет себе среда для «взрослых» компьютеров. Платная версия Visual Micro позволяет еще и удобно отлаживать код, но я довольствовался бесплатным вариантом.

Структура проекта

Проект состоит из следующих файлов:

Структура проекта в Visual Studio

Файл

Назначение

WaterpoolManager.ino

Объявление основных переменных и констант. Инициализация. Главный цикл.

HeaterMainLogic.ino

Основная логика управления реле котла (по температурам) и вспомогательными реле.

Sensors.ino

Считывание данных сенсоров

Settings.ino

Настройки устройства, сохранение их в флеш-памяти контроллера

LCD.ino

Вывод информации на LCD

ClockTimer.ino

Считывание показаний часов RTC, или симуляция часов

Relays.ino

Управление включением/выключением реле

ButtonLogic.ino

Логика реакции на состояния аппаратных кнопок

ReadButtonStates.ino

Считывание состояний аппаратных кнопок

EEPROM_Logging.ino

Логгирование данных датчиков в EEPROM

WebServer.ino

Встроенный веб-сервер для управления устройством и отображением состояний

WebPages

В этой папке хранятся страницы веб-сервера

index.h

Основная страница отображения состояния устройства. Идет считывание текущего состояния с помощью вызова ajax. Refresh каждые 5 секунд.

loggraph.h

Выводит лог данных датчиков и состояний реле в виде графика. Используется библиотека jqPlot – все построение происходит на стороне клиента. Запрос к контроллеру идет лишь на бинарный файл – копии данных из EEPROM.

logtable.h

тоже, но в виде таблицы

settings.h

Управление настройками устройства: установка пределов по температурам, потоку воды, периодичности логгирования данных

time.h

Установка текущего времени

Библиотеки

EepromLogger.cpp

Библиотека записи логов во флеш

EepromLogger.h

crc8.cpp

Подсчет 8-битного CRC для библиотеки

crc8.h

TimeSpan.cpp

Структура для управления отрезками времени

TimeSpan.h

Опрос датчиков

Датчики заносятся в порядке их отклика по шине и порядок в дальнейшем не меняется. При старте устройства происходит поиск датчиков температуры по шине OneWire и занесение их адресов в массив tempSensAddr. Запоминается индекс последнего датчика в массиве (устройство умеет работать с 4мя или меньшим количеством датчиков):

while (ds.search(tempSensAddr[lastSensorIndex]) && lastSensorIndex < 4)
if (OneWire::crc8(tempSensAddr[lastSensorIndex], 7) != tempSensAddr[lastSensorIndex][7]) { Serial.print(" CRC is not valid!"); } else lastSensorIndex++; Serial.println();
}
ds.reset_search();
lastSensorIndex--;
Serial.print("\r\nTemperature sensor count: ");
Serial.print(lastSensorIndex + 1, DEC);
Кроме того, с целью диагностики опрашивается их состояние (температуры). Основные данные при инициализации выводятся в Serial и на LCD для диагностики:
// Read sensor values and print temperatures
ds.reset();
ds.write(0xCC, TEMP_SENSOR_POWER_MODE); // Request all sensors at the one time
ds.write(0x44, TEMP_SENSOR_POWER_MODE); // Acquire temperatures
delay(1000); // Delay is required by temp. sensors char tempString[10];
for (byte addr = 0; addr <= lastSensorIndex; addr++) { ds.reset(); ds.select(tempSensAddr[addr]); ds.write(0xBE, TEMP_SENSOR_POWER_MODE); // Read Scratchpad tempData[addr] = ds.read() | (ds.read() << 8); // Read first 2 bytes which carry temperature data int tempInCelsius = (tempData[addr] + 8) >> 4; // In celsius, with math rounding Serial.print(tempInCelsius, DEC); // Print temperature Serial.println(" C");
}

Поэтому в коде введена задержка с небольшим запасом. Согласно даташиту, датчики требуют не менее 750 ms задержки между запросом значения температуры и получением ответа от датчика.

Поэтому был написан следующий хитрый код, вызываемый каждые 50 ms по таймеру: Однако, эта задержка, когда всё устройство просто ждет ответа, приемлема на старте, но абсолютно неуместно ждать каждый раз при регулярном опросе датчиков.

#define TEMP_MEASURE_PERIOD 20 // Time of measuring, * TEMP_TIMER_PERIODICITY ms
#define TEMP_TIMER_PERIODICITY 50 // Periodicity of timer calling, ms timer.attach_ms(TEMP_TIMER_PERIODICITY, tempReadTimer);
int tempMeasureCycleCount = 0; void tempReadTimer() // Called many times in second, perform only one small operation per call
{ tempMeasureCycleCount++; if (tempMeasureCycleCount >= TEMP_MEASURE_PERIOD) { tempMeasureCycleCount = 0; // Start cycle again } if (tempMeasureCycleCount == 0) { ds.reset(); ds.write(0xCC, TEMP_SENSOR_POWER_MODE); // Request all sensors at the one time ds.write(0x44, TEMP_SENSOR_POWER_MODE); // Acquire temperatures } // Between phases above and below should be > 750 ms int addr = TEMP_MEASURE_PERIOD - tempMeasureCycleCount - 1; if (addr >= 0 && addr <= lastSensorIndex) { ds.reset(); ds.select(tempSensAddr[addr]); ds.write(0xBE, TEMP_SENSOR_POWER_MODE); // Read Scratchpad tempData[addr] = ds.read() | (ds.read() << 8); // Read first 2 bytes which carry temperature data }
}

После того как проходит около 50 таких циклов (а в сумме это 50*20 = 1000 ms = 1 sec), происходит чтение значения каждого датчика, по одному за раз. В начале каждого цикла tempMeasureCycleCount происходит запрос к датчикам на чтение их значений. Вся работа разбита на кусочки, чтобы код, работающий в прерывании по таймеру, не отнимал много времени у контроллера.

По прерыванию на пине, на котором висит датчик мы увеличиваем значение счетчика тиков, пришедших с датчика потока: Значение датчика потока вычисляется следующим образом.

pinMode(FLOW_SENSOR_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(FLOW_SENSOR_PIN), flow, RISING); // Setup Interrupt
volatile int flow_frequency; // Flow sensor pulses
int flowMeasureCycleCount = 0; void flow() // Flow sensor interrupt function
{ flow_frequency++;
}

В том же таймере, где опрашиваются сенсоры температуры, раз в секунду мы берем это значение тиков и переводим в литры, используя константу FLOW_SENSOR_CONST, значение которой можно найти в характеристиках датчика:

flowMeasureCycleCount++;
if (flowMeasureCycleCount >= 1000 / TEMP_TIMER_PERIODICITY)
{ flowMeasureCycleCount = 0; litersInMinute = (flow_frequency / FLOW_SENSOR_CONST); // Pulse frequency (Hz) = FLOW_SENSOR_CONST*Q, Q is flow rate in L/min. flow_frequency = 0; // Reset Counter
}

Логгирование данных от датчиков и о состоянии прибора

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

После некоторого «кумекания» была придумана и реализована следующая модель записи:

Каждый record представляет собой запись, содержащую информацию о текущем значении потока воды, температурах датчиков, а также закодированного в байте состояния устройства (отдельные биты обозначают, включено реле или нет, разрешен ли подогрев или нет):

struct LogEvent
{ unsigned char litersInMinute = 0; unsigned char tempCelsius[4]{ 0, 0, 0, 0 }; unsigned char deviceStatus = 0;
}

После каждой записи идет байт контрольной суммы CRC, показывающий, корректно ли записана запись и вообще, было ли записано хоть что-нибудь в этой ячейке памяти.

Timestamp для каждого блока записывается только один раз, для остальных – вычисляется на основе информации о периодичности логгирования. Поскольку было бы слишком накладно по объему записывать данные о текущем времени (timestamp) для каждой записи, то данные организуются в большие блоки, по N записей в каждом.

unsigned int logRecordsInBlock = 60 * 60 / loggingPeriodSeconds; // 1 block for hour
unsigned int block_size = sizeof(Block_Header) + logRecordsInBlock * (record_size + crcSize);
unsigned int block_count = total_storage_size / block_size;

Всего у нас поместиться 39 блоков в памяти флешки размером в 32 килобайта. Например, при периодичности логгирования раз в 30 секунд, мы будем иметь 120 записей в блоке, а размер блока будет равен около 840 байт. При такой организации получается, что каждый блок начинается по строго определенному адресу в памяти, и «пробежать» по всем блокам – не проблема.

в котором отсутствует часть записей). Соответственно, при внезапном обрыве записи при прошлом выключении устройства, мы будем иметь недописанный блок (т.е. И продолжает запись, начиная со следующего блока. При включении устройства, алгоритм ищет последний по времени валидный заголовок блока (timestamp+crc). Запись осуществляется циклически: самый свежий блок перезатирает данные самого старого блока.

Невалидные блоки (те, у которых не проходит проверка CRC для timestamp) игнорируются целиком. При чтении идет последовательное чтение всех блоков. той, на которой оборвалась запись в прошлый раз, если блок не был записан целиком). Записи в каждом блоке читаются до момента встречи первой невалидной записи (т.е. Остальные игнорируются.
Для каждой записи вычисляется актуальное для неё время, основанное на timestamp блока и порядковом номере записи в блоке.

LCD

В первой строке выводится актуальная информация о текущих значениях датчиков: потока и температурах. В устройстве использован дисплей QC1602A, способный отображать 2 строчки по 16 символов. Вторая строчка показывает состояния реле нагрева и насоса, а также время, прошедшее с момента включения или выключения нагрева. В случае превышения заданного лимита, возле значения появляется восклицательный знак. Фотографии дисплея в различных режимах приведены в конце публикации. Каждые 5 секунд дисплей во второй строке кратковременно показывает актуальные лимиты.

Графики

При запросе через встроенный веб-сервер данные логгинга читаются в бинарном виде с помощью JavaScript:

var xhttp = new XMLHttpRequest();
xhttp.open("GET", "logs.bin", true);
xhttp.responseType = "arraybuffer";
xhttp.onprogress = updateProgress;
xhttp.onload = function (oEvent) { var arrayBuffer = xhttp.response; if (arrayBuffer) { var byteArray = new Uint8Array(arrayBuffer); …
}};
xhttp.send(null);

Чтение их в каком-нибудь популярном недвоичном формате, например ajax, было бы непозволительной роскошью для контроллера, прежде всего из-за большого объема, который должен был бы вернуть встроенный http сервер.

По этой же причине, для построения графиков используется JavaScript библиотека jqPlot, а сами файлы JS библиотек подгружаются с популярных CDN.

Пример графика работы устройства:

Где-то около 10:20 котел переключился на нагрев горячей воды в доме, температура контура отопления упала. Наглядно видно, что около 9:35 устройство было включено на подогрев, котел плавно начал нагревать контур отопления (датчики T3, T4), вслед за этим начала расти температура контура бассейна (датчики T1, T2). В 10:50 произошла авария: внезапно отключили насос циркуляции воды в бассейне. Затем еще через 10 минут котел вернулся к нагреву воды бассейна. Но устройство по-прежнему осталось в состоянии нагрева (красная линия на 2м графике). Поток воды резко упал до ноля, реле нагрева выключилось (красный пунктир на 2м графике), предотвращая перегрев. если бы насос был снова был включен, и температуры были бы в норме, устройство вернулось бы к нагреву. Т.е. И если бы не резкое отключение котла, была бы беда. Замечу, что после аварийного выключения насоса, температуры в контуре воды бассейна (T1, T2) начали резко расти за счет перегрева теплообменника.

Встроенный веб-сервер

При старте устройства, оно инициализируется в качестве точки доступа с дефолтным паролем, заданным в #define AP_PASS. Для общения с внешним миром используется стандартный класс ESP8266WebServer. После ввода пароля устройство перезагружается и коннектится к заданной точке доступа. Автоматически открывается web-страница для выбора доступной сети wi-fi и ввода пароля.

Готовое устройство

В ней было выпилено отверстие для LCD, и отверстия для разъемов. Готовое устройство было помещено в стандартную разделочную коробку для электропроводки.

Фотографии фасада устройства в разных режимах

С отображением времени, прошедшего после включения:

С отображением лимитов:

Заключение

Так же в некоторой степени пригодились знания HTML5, JavaScript, навыки отладки скриптов в браузере. В заключение хочу сказать, что, разрабатывая подобное устройство, я получил отличный опыт с схемотехнике, разработке печатных плат, навыки монтажа SMD компонент, в архитектуре и программировании микроконтроллеров, вспомнил почти уже забытый C++ и бережному обращению памятью и прочим ограниченным ресурсам контроллера.

А исходные коды устройства, принципиальной схемы, печатных плат – пожалуйста, пользуйтесь, дорабатывайте. Эти скилы и удовольствие, полученное при разработке устройства, – и есть основные полученные бенефиты. Аппаратная часть в общедоступном проекте на EasyEDA. Все исходные коды проекта лежат на GitHab. Даташиты к чипам, используемым в проекте, я собрал на сетевом диске.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Паркур, танцы и работа на стройке от Boston Dynamics

За последнюю неделю небезызвестная компания Boston Dynamics показала много чего интересного. Давайте вкратце посмотрим на успехи наших будущих хозяев помощников.Неделю назад появилось новое видео про гуманоидного робота Atlas, который учится паркуру: Программное обеспечение управления использует все тело, включая ноги, руки ...

Работа с изображениями на Python

Тема сегодняшнего разговора — чему же научился Python за все годы своего существования в работе с изображениями. И действительно, кроме старичков родом из 1990 года ImageMagick и GraphicsMagick, есть современные эффективные библиотеки. Например, Pillow и более производительная Pillow-SIMD. Их активный разработчик Александр Карпинский (homm) на MoscowPython сравнил разные библиотеки для работы ...