Итак, надоело! Даже, не так, ДОСТАЛО! А точнее, за.... Впрочем, не буду прибегать к ненормативной лексике. Квартира, купленная 2.5 года назад - это не квартира, а какое-то вечное испытание. Да такое, что Форт Баярд отдыхает. Кроме того, в конце игры Баярда можно нехило поднять золотишка, а чем тут все закончится - я еще не знаю. Недостаточная (ниже норм, утвержденных Правительством РФ, а эти нормы, скажем так, разрабатывались моржами) температура воздуха в помещении, отсутствие горячей воды по утрам, а ежедневное принятие душа превращается в соревнование "кто быстрей", ибо вода неожиданно может стать кипятком. Или, наоборот, льдом. Бездействие управляющей компании, сбор инициативной группы, объявления по подъездам, коллективная жалоба... это и многие другие веселые подробности останутся за рамками повествования.

Итак, если какой-то параметр, будь то температура или давление, не устраивает, то этот параметр надо для начала измерить. Желательно, в автоматическом режиме, непрерывно и с сохранением всех значений. И тут на помощь приходят средства промышленной автоматизации - датчик давления, датчик температуры, и программируемый логический контроллер с модулем аналоговых входов и поддержкой протокола HART. Почему именно они? Во-первых, я АСУшник, а не электронщик. Во-вторых, я исполняю обязанности руководителя технической группы Управления "Цифровое производство" уральского филиала компании ООО "Сименс". А, стало быть, все эти игрушки у меня есть. Этого оказалось вполне достаточно, чтобы продумать, собрать на коленке и запрограммировать систему, которой я дал замысловатое название Мобильная Установка Доказательства Актуальности Контроля Измерений (весь смысл замысловатости заключается в получившейся аббревиатуре... настолько я огорчен сложившейся ситуацией).

В качестве датчика, измеряющего давление, выступает Siemens Sitrans P DS3 с диапазоном измерения от 0 до 16 бар, выходом 4..20 мА и, что немаловажно, поддержкой цифрового протокола HART. HART означает Highway Addressable Remote Transducer. По сути, это цифровой протокол обмена данными между системой управления и датчиком. Обмен данными двухсторонний. Для кодирования нулей и единичек цифрового обмена используется частотная модуляция, которая накладывается прямо поверх аналогового сигнала 4..20 мА. Поддержка HART сильно играет мне на руку, поскольку позволяет избежать дополнительного аналого-цифрового преобразования на стороне ПЛК и, скажем откровенно, "забить" на "землю" (боюсь, что после этих слов мне коллеги руки не подадут). На фотографии этот красавец уже подключен к домашнему водопроводу.

Датчик температуры - тоже Siemens, тоже с HART'ом. Вторичка - Sitrans TH300, первичка - обычный Pt100 в толстой гильзе. Именно из-за гильзы он не подошел для замеров температуры воздуха - просто огромная инерция измерений.

В качестве CPU применяется S7-1510 (он же ET200SP CPU), заказной номер 6ES7 510-1DJ01-0AB0, версия прошивки 2.8. К CPU справа подключен четырехканальный модуль аналоговых входов с поддержкой HART, заказной номер 6ES7 134-6TD00-0CA1. Блок питания слева от контроллера, весьма большой и очень избыточный по мощности в моем случае.

Для визуализации измеряемых параметров и, что немаловажно, для их архивации взял базовую операторскую панель второго поколения, KTP400 Basic, 6AV2 123-2DB03-0AX0, прошивка обновлена до 16.0. Причина для такого выбора простая - мобильных панелей у меня под рукой нет, а "немобильные", стандартные версии предназначены для врезки в дверь шкафа управления, но никак не для того, чтобы держать их в руках. Маленькую четырехдюймовую панель на весу удержать можно без проблем, а вот семидюймовую Comfort или Unified одной рукой не удержишь. Разбивать подотчетное оборудование и попадать на 1200 евро её листовой стоимости не хочется никак, поэтому лучше маленькую и легкую, чем большую, тяжелую, но функциональную.

Для обвязки применяется обычный провод сечением 0.5 мм2 и НШВИ 0.5х8. Техническая эстетика не соблюдается никак. Корпуса нет. Заземляющих проводников нет. Все обвязано, абы как. Прикладное ПО далеко не универсально, уставок от оператора нет, даже константы не применяются, все тупо в лоб. В общем, это ни в коем случае не готовое решение, которое можно сертифицировать и вносить в реестр СИ. Я напишу про это тут один раз, дабы избежать войн в комментариях, да, со всеми этими замечаниями я согласен.

Датчики подключаем к каналам 0 и 1 аналогового модуля в соответствии с документацией.

У нас используется вариант (1) - двухпроводное подключение датчика. Под "двухпроводностью" подразумевается, что к датчику в общем случае идет всего два провода, "+" и "-" непосредственно от аналогового модуля. Отдельного питания на датчик не требуется, он запитывается от модуля AI. Электроника модуля получает энергию по внутренней шине контроллера, а "силовая" часть подается на отдельные клеммы. В итоге, первый датчик (датчик давления) садим на контакты №9 и 13 (нулевой канал), датчик температуры - №№10 и 14 (первый канал).

Для программирования и конфигурирования этой системы применяем среду разработки TIA Portal V16 (+update 3), в составе Step 7 Professional (контроллер) и WinCC Basic (операторская панель). После создания проекта в первую очередь создаем и конфигурируем ПЛК.

Включаем System and Clock Memory, пригодится.

Задаем IP адрес CPU - 192.168.43.205. Конфигурируем аналоговый модуль. Второй и третий каналы отключаем, на нулевом и первом включаем HART и активируем всю диагностику (диагностика совсем необязательна в моем простом случае).

Обзор настройки каналов модуля
Обзор настройки каналов модуля
Настройка нулевого (и первого тоже) канала
Настройка нулевого (и первого тоже) канала

Поскольку в данном случае задействовано всего 2 канала, а от функционала HART требуется всего лишь передача значения измеряемой величины, то можно поступить просто и забирать первичную переменную HART обоих датчиков через пространство ввода/вывода аналогового модуля. Тем самым избавляемся от необходимости осуществлять системный вызов в программе PLC.

Из этой таблицы видно, что нам надо забрать две переменные с адресов ID8 и ID13. Первичные переменные HART будут поступать нам, как готовые вещественные величины. Пропишем эти адреса в Tag Table. Так же я еще прописал все четыре аналоговых канала модуля, но в программе они никак не используются... просто привычка объявлять переменные по каналам, которые есть в проекте.

Посмотрим, что я начудил в программе контроллера. Вообще, в ПЛК можно обойтись без какой-либо программы вообще. Информация с датчиков и так автоматически поступает в переменные с именами CH0_HART (давление) и CH1_HART (температура). Однако, установка изначально разрабатывалась, как универсальная - она замеряет и Т воздуха, и Т воды, и давление воды, да еще и в нескольких местах (воздух в разных комнатах, например). Если обойтись одним трендом, то можно уже через день запутаться, что, где и когда измерял. Поэтому программа контроллеру тут носит больше "организационный" характер.

Все необходимые переменные сведены в один блок данных.

Вся программа написана в "главном" огранизационном блоке. Расписывать ее нет необходимости, можно ознакомиться с комментариями в коде.

"Data".CurrentPress := "Ch0_HART" / 10.0; //давление перевести из бар в МПа
"Data".CurrentTemp := "Ch1_HART"; //текущая температура

IF "Data".MeasP THEN //если включен (с панели) замер давления
    
    //допускается всего 4 места измерения давления - в ванной (ХВС, ГВС) и на кухне
    IF "Data".WaterSelector <= 0 OR "Data".WaterSelector > 4 THEN 
        "Data".WaterSelector := 1;
    END_IF;
    
    //создать для панели сообщение "идет замер давления в/на <место замера и выбранная труба">
    CASE "Data".WaterSelector OF
        1: //ХВС в ванной
            "Data".WaterMessage := W#2#0000000000000001;
        2: //ГВС в ванной
            "Data".WaterMessage := W#2#0000000000000010;
        3: //ХВС на кузне
            "Data".WaterMessage := W#2#0000000000000100;
        4: //ГВС на кухне
            "Data".WaterMessage := W#2#0000000000001000;
    END_CASE;
    
    //тнекщее давление в МПа скопировать в отдельную переменную для тренда
    "Data".CurrentPress_M := "Data".CurrentPress;
    
    //если замер только что включили, сбросить минимальное и максимальное зафиксированное значение
    IF (NOT "Data".MeasP_prv) THEN
        "Data".MinPress_M := "Data".CurrentPress_M;
        "Data".MaxPress_M := "Data".CurrentPress_M;
    END_IF;
    
    //зафиксировать при необходимости минимальное значение
    IF "Data".CurrentPress_M < "Data".MinPress_M THEN
        "Data".MinPress_M := "Data".CurrentPress_M;
    END_IF;
    
    //зафиксировать максимальное значение давления во время этого замера
    IF "Data".CurrentPress_M > "Data".MaxPress_M THEN
        "Data".MaxPress_M := "Data".CurrentPress_M;
    END_IF;
    
    //выставить аварийные и предупредительные сообщения
    "Data".P003Alarm_M := "Data".CurrentPress_M < 0.03;
    "Data".P01Alarm_M := "Data".CurrentPress_M < 0.1;
    "Data".P02Alarm_M := "Data".CurrentPress_M < 0.2;
ELSE //если замер давления не ведется
    "Data".CurrentPress_M := 0.0; //обнулить переменную для тренад давления
    "Data".WaterMessage := W#2#0000000000000000; //сбросить "место проведения давления"
    "Data".P003Alarm_M := FALSE; //сбросить флаги алармов и сообщений
    "Data".P01Alarm_M := FALSE;;
    "Data".P02Alarm_M := FALSE;;
END_IF;

IF "Data".MeasTair THEN //аналогично для температуры воздуха
    
    IF "Data".PlaceSelector <= 0 OR "Data".PlaceSelector > 7 THEN
        "Data".PlaceSelector := 7;
    END_IF;
    
    CASE "Data".PlaceSelector OF
        1: //спальня
            "Data".PlaceMessage := W#2#0000000000000001;
        2: //детская
            "Data".PlaceMessage := W#2#0000000000000010;
        3: //зал
            "Data".PlaceMessage := W#2#0000000000000100;
        4: //кухня
            "Data".PlaceMessage := W#2#0000000000001000;
        5: //ванная
            "Data".PlaceMessage := W#2#0000000000010000;
        6: //туалет
            "Data".PlaceMessage := W#2#0000000000100000;
        7: //прочее
            "Data".PlaceMessage := W#2#0000000001000000;
    END_CASE;
    
    "Data".CurrentTemp_Mair := "Data".CurrentTemp;
    
    IF (NOT "Data".MeasTair_prv) THEN
        "Data".MinTemp_Mair := "Data".CurrentTemp_Mair;
        "Data".MaxTemp_Mair := "Data".CurrentTemp_Mair;
        "Data".MeasTwater := false;
    END_IF;
    
    IF "Data".CurrentTemp_Mair < "Data".MinTemp_Mair THEN
        "Data".MinTemp_Mair := "Data".CurrentTemp_Mair;
    END_IF;
    
    IF "Data".CurrentTemp_Mair > "Data".MaxTemp_Mair THEN
        "Data".MaxTemp_Mair := "Data".CurrentTemp_Mair;
    END_IF;
    
    "Data".T18Alarm_M := "Data".CurrentTemp < 18;
    "Data".T20Alarm_M := "Data".CurrentTemp < 20;
    "Data".T22Alarm_M := "Data".CurrentTemp < 22;
ELSE
    "Data".CurrentTemp_Mair := 0.0;
    "Data".PlaceMessage := W#2#0000000000000000;
    "Data".T18Alarm_M := FALSE;
    "Data".T20Alarm_M := FALSE;
    "Data".T22Alarm_M := FALSE;
END_IF;

//аналогично для температуры воды
IF "Data".MeasTwater THEN
    "Data".CurrentTemp_Mwater := "Data".CurrentTemp;
    
    IF (NOT "Data".MeasTwater_prv) THEN
        "Data".MinTemp_Mwater := "Data".CurrentTemp_Mwater;
        "Data".MaxTemp_Mwater := "Data".CurrentTemp_Mwater;
        "Data".MeasTair := false;
    END_IF;
    
    IF "Data".CurrentTemp_Mwater < "Data".MinTemp_Mwater THEN
        "Data".MinTemp_Mwater := "Data".CurrentTemp_Mwater;
    END_IF;
    
    IF "Data".CurrentTemp_Mwater > "Data".MaxTemp_Mwater THEN
        "Data".MaxTemp_Mwater := "Data".CurrentTemp_Mwater;
    END_IF;
    
    "Data".T40Alarm_M := "Data".CurrentTemp < 40;
    "Data".T55Alarm_M := "Data".CurrentTemp < 55;
    "Data".T57Alarm_M := "Data".CurrentTemp < 57;
    "Data".T60Alarm_M := "Data".CurrentTemp < 60;
ELSE
    "Data".CurrentTemp_Mwater := 0.0;
    "Data".T40Alarm_M := FALSE;
    "Data".T55Alarm_M := FALSE;
    "Data".T57Alarm_M := FALSE;
    "Data".T60Alarm_M := FALSE;
END_IF;

//при первом запуске контроллера сбросить зафисированные мин и макс "общих" (не замерных) величин
"tonFirstScan".TOF(IN:=NOT "FirstScan",
                   PT:=T#1s);

IF "tonFirstScan".Q THEN
    IF "Data".CurrentPress < "Data".MinPress THEN
        "Data".MinPress := "Data".CurrentPress;
    END_IF;
    
    IF "Data".CurrentPress > "Data".MaxPress THEN
        "Data".MaxPress := "Data".CurrentPress;
    END_IF;
    
    IF "Data".CurrentTemp < "Data".MinTemp THEN
        "Data".MinTemp := "Data".CurrentTemp;
    END_IF;
    
    IF "Data".CurrentTemp > "Data".MaxTemp THEN
        "Data".MaxTemp := "Data".CurrentTemp;
    END_IF;
END_IF;

//сбросить мин и макс по запросу с панели
IF "Data".ResetPressStat THEN
    "Data".MinPress := "Data".CurrentPress;
    "Data".MaxPress := "Data".CurrentPress;
    "Data".ResetPressStat := FALSE;
END_IF;

IF "Data".ResetTempStat THEN
    "Data".MinTemp := "Data".CurrentTemp;
    "Data".MaxTemp := "Data".CurrentTemp;
    "Data".ResetTempStat := FALSE;
END_IF;

//выставить обобщенный флаг "идет замер"
"Data".Meas := "Data".MeasP OR "Data".MeasTair OR "Data".MeasTwater;

//для определения фронтов
"Data".MeasP_prv := "Data".MeasP;
"Data".MeasTair_prv := "Data".MeasTair;
"Data".MeasTwater_prv := "Data".MeasTwater;

//сообщения для панели оператора
"Data".Alarms.%X0 := "Data".MeasP;
"Data".Alarms.%X1 := "Data".MeasTair;
"Data".Alarms.%X2 := "Data".MeasTwater;

"Data".Alarms.%X3 := "Data".T18Alarm_M;
"Data".Alarms.%X4 := "Data".T20Alarm_M;
"Data".Alarms.%X5 := "Data".T40Alarm_M;
"Data".Alarms.%X6 := "Data".P003Alarm_M;
"Data".Alarms.%X7 := "Data".P01Alarm_M;
"Data".Alarms.%X8 := "Data".P02Alarm_M;
"Data".Alarms.%X9 := "Data".T22Alarm_M;
"Data".Alarms.%X10 := "Data".T55Alarm_M;
"Data".Alarms.%X11 := "Data".T57Alarm_M;
"Data".Alarms.%X12 := "Data".T60Alarm_M;

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

Добавим некоторые настройки рантайма - выбор бита для списков и цвет алармов в зависимости от их класса.

Создадим соединение с контроллером и вытащим необходимые тэги (все это делается автоматически при вытаскивании тэга с ПЛК на любой экран панели). Выставим минимально возможное время опроса переменных в 100 мс.

Все тэги берем с блока данных 1 ПЛК
Все тэги берем с блока данных 1 ПЛК

Для тэгов Data_Alarms, DataWaterMessage и Data_PlaceMessage создадим сообщения.

Создадим Alarm Log для сообщений панели на максимальное количество записей, 500000. Весь лог будет храниться в текстовом виде на USB-флэшке, подключенной к панельке. Журнал циклический, по достижению предельного количества записей, старые будут затираться.

В настройках классов сообщений укажем, алармы каких классов будут сохраняться на фдэшке

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

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

Кнопкой F1 листаем экраны замеров. Остановися только на экране давления, два других аналогичны. На этом экране отображается текущее мгновенное значение давления при идущем замере, эксремумы давления, выбор места проведения измерения и график с историческими данными. Кнопкой F3 начинаем замер давления, кнопкой F4 останавливаем,

На любом экране кнопкой F2 открывается журнал событий.

Дополнительно для собственного удобство я сделал копию этой панели и изменил ее тип на PC Station с одиночной SCADA-системой WinCC RT Advanced. На 4дюймовом экране очень неудобно смотреть графики.

Зато, на 24 дюймах смотреть удобно

Установка находится в работе с 21 декабря. За это время мне стало изместно время пикового вечернего разбора воды, это в районе 22 часов местного времени. Ситуация удручающая, давление падает ниже установленных норм (0.03МПа) и падает влоть до нуля. Да и в целом давление не постоянное, оно может за секунду упасть с трех килограммов до одного, а потом за 5 секунд подняться до 2.5 килограмм.

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

Следующим шагом будет установка к линейку ПЛК WiFi модуля, настройка WiFi-моста и создание полноценного операторского интерфейса на базе WinCC OA под управлением Linux Debian (Buster).