Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
В этой серии статей я расскажу как самостоятельно собрать полнофункциональный прототип промышленного IIoT-шлюза на базе Raspberry PI.
Разумеется, подобная штука не может стать полноценной заменой настоящему промышленному железу - достичь сравнимого уровня надежности, защищенности и производительности одновременно, либо не получится, либо будет намного сложнее и дороже, чем купить готовую железку.
Однако в качестве быстрого и дешевого решения на этапе проверки гипотез (в момент когда вам только предстоит определиться какие данные каким способом снимать и как их потом хранить и использовать) такое решение вполне имеет право на существование.
В конце концов, с программной точки зрения, большинство современных промышленных IoT-шлюзов - не что иное, как обычные одноплатные ПК со специфической ОС (чаще всего на базе Linux) и набором предустановленного ПО.
В общем те, кто готов к подобным экспериментам на производстве, либо просто интересуется IIoT и хочет поэкспериментировать с технологиями для собственного развития - вэлкам под кат!
Постановка задачи
Для начала давайте определим, какие функции мы хотим получить на выходе.
Основная суть подобного проекта - анализ данных о производственном процессе.
То есть в первую очередь - сбор телеметрии с различных датчиков, отправка её на сервер (например в облако) для централизованной обработки и накопление истории. Дальнейшая судьба собранной статистики зависит от конкретной решаемой задачи и вашей фантазии.
Также неплохо было бы иметь возможность мониторить поступающие данные в реальном времени (хотя бы чтобы понять идут они или нет) и накапливать локальный архив timeseries на шлюзе (например, если нет соединения с интернетом).
В первой части статьи мы реализуем основные функции:
сбор телеметрии с промышленных датчиков по протоколу Modbus;
передачу данных в облако;
локальный мониторинг в реальном времени;
Во второй - разберемся с тем, как можно накапливать и обрабатывать телеметрию в облаке;
А в третьей - доработаем проект дополнительными фичами:
локальным хранилищем телеметрии;
устройством для прямого считывания аналогового или цифрового сигнала;
Общая архитектура
Наш небольшой прототип будет иметь все обязательные компоненты IIoT-решения:
Устройство считывания показаний датчиков (можно использовать промышленный контроллер, “умные” датчики, или собрать свой вариант на базе любого arduino-совместимого контроллера);
IIoT-шлюз, в качестве которого будет выступать Raspberry PI;
Облачный сервис, который принимает данные по протоколу MQTT, сохраняет их в Managed DB и производит дальнейшую обработку - эту часть развернем на платформе Yandex.Cloud
Настраиваем шлюз
Начнем с центрального узла нашего небольшого проекта, то есть малинки.
В качестве “ядра” системы на стороне шлюза удобно использовать Node-RED. Это простой и удобный инструмент Low-code разработки, который позволяет сократить время создания IoT (и не только) решений. Если по какой-то неведомой причине вы им ещё не пользуетесь - обязательно почитайте про него тут и тут!
Одно из главных преимуществ Node-RED - наличие огромного количества расширений. В том числе, специальных “кубиков” для работы с modbus, serial и всевозможными базами данных. Там же есть и конструктор легких дашбордов для real-time мониторинга (всё это нам понадобится).
1) Устанавливаем и настраиваем Node-RED:
Вообще Node-RED есть в официальном репозитории Raspberry и его можно поставить просто через apt-get. Но разработчики рекомендуют использовать специально подготовленный ими скрипт, который сразу ставит ещё и npm и настраивает node-RED для запуска в качестве сервиса.
Заходим на малинку и запускаем скрипт:
$ bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
Дожидаемся завершения установки (она может занять несколько минут). Когда установка завершена, можно сразу настроить автозапуск при старте ОС:
$ sudo systemctl enable nodered.service
Если хотите сразу озаботиться некоторой безопасностью, можно настроить вход в web-интерфейс по паролю вот по этой инструкции.
Spoiler
Файл settings.js скорее всего будет находиться в папке: /home/pi/.node-red
Теперь всё готово к запуску Nede-RED:
$ node-red-start
Если всё установилось и запустилось успешно, web-интерфейс Node-RED будет доступен в локальной сети по адресу: [IP малинки]:1880
Но настройка на этом ещё не закончена - по умолчанию у нас есть только базовые предустановленные компоненты, а для задуманного понадобится несколько дополнительных, которые надо еще поставить.
Заходим в web-интерфейс, идем в настройки палитры: [Меню в правом верхнем углу] ->[Pallet manager]:
Переходим во вкладку “Install”, находим и устанавливаем следующие пакеты:
node-red-contrib-modbus - пакет для работы по протоколу Modbus
node-red-dashboard - дашборды для мониторинга в реальном времени
postgrestor - простой компонент для выполнения запросов к PostgreSQL
node-red-node-serialport - компонент для работы с serial (этот компонент может быть уже установлен вместе с базовыми)
Вот теперь Node-RED настроен, можно приступать к разработке!
2) Реализуем считывание данных по Modbus:
Modbus - открытый протокол, разработанный ещё в 1979 году для использования в контроллерах MODICON (бренд, между прочим, жив до сих пор и ныне принадлежит Schneider Electric).
Сейчас modbus является де-факто стандартом для промышленных сетей и поддерживается подавляющим большинством контроллеров и “умных” датчиков. Для интересующихся, вот тут есть хорошая обзорная статья по теме.
Я же не буду подробно останавливаться на его описании. Упомяну только что протокол имеет 3 основные модификации:
две для использования с сетевыми интерфейсами RS-232/422/485 (Modbus ASCII и Modbus RTU)
и одну для обмена по TCP/IP (Modbus TCP)
Это важно, так как с одной стороны влияет на то, как Raspberry будет физически подключаться к устройствам (в первом случае понадобится переходник COM/RS-USB), а с другой - от этого зависят настройки считывания данных.
И так, подключаем девайс в соответствующее гнездо малины, создаем поток, добавляем в него кубик “modbus-read” и заходим в его настройки:
Для начала надо создать подключение к Modbus-серверу. Нажимаем “Создать подключение” и вводим параметры устройства, с которого хотим получать телеметрию:
Для варианта с RS (Serial) необходимо указать адрес порта, к которому подключено устройство, тип протокола (RTU или ASCII) и поддерживаемую скорость обмена (baud rate):
Для TCP указываем IP-адрес устройства (стоит убедиться, что для eth0 на малинке настроена статика в той же подсети и устройство успешно пингуется) и номер порта (обычно используется порт 502):
Теперь настраиваем сам кубик. Тут важны 4 параметра:
FC (код функции) - Для считывания цифровых данных - 02, для аналоговых - 04.
Address - адрес первого регистра для считывания. Адреса регистров можно посмотреть в спецификации устройства, обычно “смысловые” регистры идут в начале, т.е. Начинаются непосредственно с 0.
Quantity - количество регистров для считывания. Этот параметр зависит от количества передаваемых устройством параметров - сколько передается сигналов столько и будет регистров. Эту информацию также можно найти в спецификации или даташите.
Poll Rate - частота опроса. Задает частоту, с которой поток будет получать данные от устройства.
В данном примере настроено получение сигналов восьми аналоговых датчиков, начиная с регистра 0000, раз в 5 секунд:
Кубик возвращает в payload массив значений регистров (если регистры указаны правильно - они же показания датчиков). Осталось их немного отформатировать и добавить метку времени. Для этого воспользуемся функцией:
var out_msg = []
var values = []
var values_formated = []
values = msg.payload;
var time = (new Date()).toISOString()
var n = 0;
for(var v in values)
{
values_formated.push({out:"A"+n.toString(), val:values[v]});
n = n+1;
}
out_msg.push({payload:{datetime:time, val:values_formated}});
return out_msg;
В данной функции каждому значению просто проставляются метки входа, но вы можете настроить преобразования на свое усмотрение - например задать масштабирование или пересчет из целочисленных значений регистров в измеряемые физические величины (температуру, напряжение, скорость и т.п). Для тестирования добавляем отладочные сообщения (чтобы посмотреть какой payload будет поститься в облако).
Нажимаем кнопку “Deploy” в правом верхнем углу. Если подключение настроено правильно, под нодой подключения к modbus появится статус “active”, а во вкладке “debug” начнут выводиться отформатированные сообщения:
3) Настраиваем мониторинг:
Теперь настроим дашбордик для мониторинга сигналов из web-интерфейса Node-RED. Для этого используем кубик “chart”.
Функционал chart позволяет отображать несколько сигналов на одном графике, но подаваться они должны по отдельности - каждый в своем сообщении со своим топиком. Поэтому массив, приходящий от устройства по modbus надо разделить на отдельные сигналы. Для этого воспользуемся ещё одной функцией:
var values = [];
var values_formated = [];
var topic_nm = "";
values = msg.payload;
var n = 0;
for(var v in values) // для каждого сигнала формируем свое сообщение
{
topic_nm = "A"+n.toString(); // формируем название сигнала
values_formated.push( // подготовленные сообщения складываем в общий массив:
{topic:topic_nm, // название сигнала - в топик
payload:values[v]}); // значение сигнала - в payload
n = n+1;
}
return {payload:values_formated};
Функция в Nod-RED может иметь больше одного выхода (количество выходов настраивается в нижней части окна свойств):
Но в данном случае мы можем не знать заранее сколько сигналов (и соответственно выходов) будет. Поэтому положим всё в один массив и вернем его целиком:
...
return {payload:values_formated};
А дальше воспользуемся нодой split для разделения сообщений и нодой "change" для их форматирования.
split - разделит массив на отдельные payload-ы, а в change мы положим топик и payload каждого сообщения на его законное место:
На выходе получаем поток отдельных сообщений в правильном формате, готовый для подачи в чарт. В результате получается вот такой flow:
Деплоим его и переходим по адресу: http://[IP малинки]:183/ui
И видим график сигналов в реальном времени:
Вот и всё, сбор и локальное отображение телеметрии настроено!
Настройка брокера MQTT в облаке
Теперь можно приступить к настройке серверной части решения. Для этого воспользуемся облачным сервисом Yandex, называемым Yandex IoT Core.
MQTT - ещё один де факто стандартный протокол для IoT-проектов. Вот тут есть хороший обзор самого протокола, поэтому подробно на нем останавливаться не буду, но немного расскажу как устроен MQTT-брокер от Yandex:
В Yandex IoT Core есть два основных типа объектов - реестры и устройства.
Реестры с одной стороны группируют устройства (в одном реестре может быть одновременно несколько устройств) а с другой - являются как бы второй стороной обмена сообщениями.
Каждый реестр имеет доступ к телеметрии своих устройств и может отправлять им команды. При этом у каждого реестра и каждого устройства есть свой набор топиков. Каждый реестр может читать и отправлять сообщения в свои топики, и в топики любого своего устройства. Аналогично каждое устройство может писать и читать свои топики и топики своего реестра (но не другого устройства, даже если оно "живет" в том же реестре).
При этом сами топики реестров и устройств никак между собой не связаны - сообщение приходит именно в тот топик, в который отправлено и в других недоступно.
Для дальнейших действий потребуется учетная запись Yandex.Cloud. Если у вас такой еще нет, ее можно достаточно быстро завести. В процессе необходимо ввести данные карты но (в отличии от некоторых других облачных сервисов) деньги с неё списываться не будут пока вы явно не переключитесь на платный аккаунт. После регистрации Yandex предоставляет небольшой грант в 4К рублей, которого на приведенные тут эксперименты хватит с лихвой.
Подключаем и настраиваем сервис:
Для начала генерируем сертификат, который будет использоваться для чтения данных из реестра. Вообще говоря, сервис позволяет идентификацию по логину и паролю как для устройств, так и для реестров, но поскольку предполагается использовать реальные данные с оборудования, лучше озаботиться идентификацией по SSL.
Сгенерировать пару сертификатов можно при помощи утилиты OpenSSL вот такой командой:
$ openssl req -x509 \
--newkey rsa:4096 \
--keyout key_reg.pem \ # имя сертификата закрытого ключа
--out crt_reg.pem \ # имя сертификата открытого ключа
--nodes \
--days 365 \
--subj '/CN=localhost'
Теперь заходим в консоль Yandex.Cloud, выбираем IoT Core и создаем новый реестр:
Открытую часть ключа, сгенерированного на предыдущем шаге (crt_reg.pem), загружаем в настройки реестра. Этот сертификаты будут использоваться для считывания телеметрии из брокера внешними сервисами и отправки команд устройствам:
Нажимаем "Создать" и попадаем в настройки свежесозданного реестра. Теперь надо зарегистрировать в нем малинку в качестве устройства.
Аналогичным образом генерируем пару сертификатов на малинке. Оба сертификата сразу кладем в отдельную папочку, туда же скачиваем корневой сертификат удостоверяющего центра. Они понадобятся для настройки отправки сообщений.
Заходим в Устройства и создаем новое:
Аналогично с созданием реестра загружаем сертификат из пары, созданной на малинке и нажимаем "Добавить".
На этом настройка брокера завершена, всё готово для приема сообщений.
ID устройств и реестров можно посмотреть на вкладке "Обзор". Они нужны для задания адресов топиков:
Топик устройства: $devices/<ID устройства>/events
Топик реестра: $registries/<ID реестра>/events
Также у каждого устройства и реестра есть перманентный топик. Основное отличие перманентных топиков состоит в том, что в них всегда сохраняется последнее сообщение. То есть при подключении консьюмер получит последнее отправленное в него сообщение, даже если не был в сети в момент его отправки. Адреса перманентных топиков похожи на обычные, но заканчиваются на state а не events:
Перманентный топик устройства: $devices/<ID устройства>/state
Перманентный топик реестра: $registries/<ID реестра>/state
Подробнее о топиках Yandex IoT Core можно почитать вот тут.
Настройка отправки сообщений по MQTT
Возвращаемся к проекту Node-RED.
Кубик для отправки сообщений по MQTT в Node-RED уже предустановлен. Добавляем его в поток после функции "add_time_stamp"и настраиваем:
Указываем адрес топика, в который собираемся писать, например топик реестра. Уровень сервиса (QoS) ставим 0 - для наших задач отслеживание доставки не требуется:
Заходим в настройки сервера. Тут настраиваем подключение к брокеру:
Сервер: mqtt.cloud.yandex.net
Порт: 8883
Ставим галочку “Enable secure (SSL/TLS) connection” и заходим в настройки TLS:
Указываем пути до файлов ключей, сгенерированных на этапе настройки IoT Core:
Сохраняем настройки и деплоим проект. В итоге flow выглядит вот так:
А телеметрия успешно отправляется в облако!
Проверить это можно с помощью утилиты командной строки yc, подписавшись на указанный в настройках топик:
$ yc iot mqtt subscribe \
--cert registry-cert.pem \ # файл сертификата реестра
--key registry-key.pem \ # файл ключа реестра
--topic '$devices/<ID устройства>/events' \
--qos 1
Либо собрав отдельный поток Nod-RED с чтением из MQTT с помощью нода "mqtt in" (интереснее, если он будет работать на другом устройстве, но и та же самая малинка тоже подойдет):
Он настраивается аналогично "mqtt out", но обратите внимание, что для чтения надо создать отдельное подключение со своей конфигурацией TLS, в которую надо загрузить уже сертификаты реестра. Подключение от имени устройства свои же сообщения читать не будет. А вот топик должен быть именно тот, в который происходит запись.
Результат
И так, минимальный функционал шлюза реализован - телеметрия собирается, передается в облако и доступна в реальном времени из любой точки планеты. Можно, например, развернуть Node-RED на вашем локальном ПК и сделать на нем небольшой дашбордик, отражающий собираемые показатели.
А в качестве бонуса уже есть небольшой локальный монитор сигналов, доступный в локальной сети малинки.
В следующей части начнем сохранять получаемую телеметрию и посмотрим что ещё с ней можно делать в облаке.
Пример потоков Node-RED, описываемых в статье, можно скачать тут.