В этой серии статей я расскажу как самостоятельно собрать полнофункциональный прототип промышленного IIoT-шлюза на базе Raspberry PI.

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

Однако в качестве быстрого и дешевого решения на этапе проверки гипотез (в момент когда вам только предстоит определиться какие данные каким способом снимать и как их потом хранить и использовать) такое решение вполне имеет право на существование.

В конце концов, с программной точки зрения, большинство современных промышленных IoT-шлюзов - не что иное, как обычные одноплатные ПК со специфической ОС (чаще всего на базе Linux) и набором предустановленного ПО.

В общем те, кто готов к подобным экспериментам на производстве, либо просто интересуется IIoT и хочет поэкспериментировать с технологиями для собственного развития - вэлкам под кат!


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

Для начала давайте определим, какие функции мы хотим получить на выходе.

Основная суть подобного проекта - анализ данных о производственном процессе.

То есть в первую очередь - сбор телеметрии с различных датчиков, отправка её на сервер (например в облако) для централизованной обработки и накопление истории. Дальнейшая судьба собранной статистики зависит от конкретной решаемой задачи и вашей фантазии.

Также неплохо было бы иметь возможность мониторить поступающие данные в реальном времени (хотя бы чтобы понять идут они или нет) и накапливать локальный архив timeseries на шлюзе (например, если нет соединения с интернетом).

В первой части статьи мы реализуем основные функции: 

  • сбор телеметрии с промышленных датчиков по протоколу Modbus;

  • передачу данных в облако;

  • локальный мониторинг в реальном времени;

Во второй - разберемся с тем, как можно накапливать и обрабатывать телеметрию в облаке;

А в третьей - доработаем проект дополнительными фичами:

  • локальным хранилищем телеметрии;

  • устройством для прямого считывания аналогового или цифрового сигнала;

Общая архитектура

Наш небольшой прототип будет иметь все обязательные компоненты IIoT-решения:

  1. Устройство считывания показаний датчиков (можно использовать промышленный контроллер, “умные” датчики, или собрать свой вариант на базе любого arduino-совместимого контроллера);

  2. IIoT-шлюз, в качестве которого будет выступать Raspberry PI;

  3. Облачный сервис, который принимает данные по протоколу 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 IoT Core
Организация топиков 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, описываемых в статье, можно скачать тут.