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


Далее я покажу каким образом я решил для себя эту задачу, используя Raspberry Pi и модуль KNX BAOS 838 kBerry от Weinzierl.



Мотивация


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


Существующие решения


На рынке можно найти решения для данной задачи, со следующими из них я плотно работал:


Loxone Miniserver


  • Цена: ~600 евро
  • Плюс: своя шина, большой ассортимент устройств.
  • Плюс: простой конфигуратор, программирование логики блоками, хорошая документация.
  • Минус: нету возможности программировать на языках высокого уровня. Есть встроенный интерпретатор PicoC, но на нем проблематично реализовать, для примера, доступ к облаку.

iRidium Server для UMC


  • Цена: рекомендуемая розничная 119000р, или ~1700 евро.
  • Плюс: поддержка большого количества систем автоматизации, помимо KNX.
  • Плюс: поддержка JavaScript.
  • Минус: файл проекта — zip архив, что усложняет его редактирование. Т.е. для того чтобы отредактировать скрипт, вам надо либо использовать iRidium Studio с встроенным редактором скриптов, в котором нету прелестей привычного вам IDE/редактора, либо придумать способ распаковки/сборки проекта.
  • Минус: JavaScript движок. Для примера, нету привычного setTimeout/setInterval, вместо них IR.SetTimeout/IR.SetInterval. Нету совместимости с nodejs.
  • Минус: стабильность работы, частые перезагрузки.

EVIKA LogicMachine


  • Цена: ~3000 евро.
  • Плюс: помимо KNX TP/UART на плате имеются интерфейсы RS-485, RS-232 и прочие, в зависимости от конфигурации.
  • Плюс: язык lua, cron расписания, и прочее.

Некоторые решения хороши, но отталкивает цена, плюс хотелось бы использовать больше гибкости в программировании.


Решение


Задача решилась с использованием Raspberry Pi и модуль Weinzierl KNX BAOS Module 838 kBerry.
Стоимость модуля ~70 евро, в сумме с Raspberry Pi + блок питания, корпус на DIN рейку выходит около 150 евро.


В качестве среды выполнения используется nodejs, из зависимостей модуль "serialport" для общения с UART.


Краткое описание протокола связи

Наше приложение подключается по серийному порту к модулю BAOS 838 и общается посредством ObjectServer protocol, описание которого можно найти по ссылке в конце статьи.


Фрейм с данными заключается в протокол FT1.2 следующим образом:


|0x68|L|L|0x68|CR|data|C|0x16|
где L - длина данных data +1 для контрольного байта
CR - контрольный байт, равен 0x73 для нечетных, 0x53 для четных фреймов,
C - чексумма = (сумма байт данных + контрольного байта) mod 256

Также есть фреймы с постоянной длиной, такие как reset request, reset indication, acknowledge frame.


Reset request отправляется при открытие соединения, reset indication отправляется с модуля baos 838 к Raspberry Pi при сбросе модуля, acknowledge отправляет каждая сторона при приеме данных.


Данные data состоят из следующих полей:


|  Поле            |  Размер  |  Описание
|  MainService     |  1       |  Главный сервис. Везде 0xF0
|  SubService      |  1       |  Сервис. Их три вида: запросы, ответы, индикация.
|  StartItem       |  2       |  ID первого элемента
|  NumberOfItems   |  2       |  Максимальное количество элементов
...          
И далее в зависимости от сервиса. Эти четыре поля присутствуют во всех сообщениях.                                                                                                                                                                             

Схема коммуникации:


  1. Наше приложение (клиент) отправляет reset request.
  2. BAOS 838 (сервер) отправляет acknowledge.
  3. Клиент отправляет первый запрос(для примера GetDatapointDescription.Req). Нечетный фрейм.
  4. Сервер получает запрос, отправляет acknowledge.
  5. Сервер отправляет ответ на запрос.
  6. Клиент отправляет подтверждение acknowledge.
    ....

Подключение


Модуль подключается к GPIO контактам платы Raspberry Pi, более подробно можно узнать по ссылкам в конце статьи.


ETS


Как и любое KNX устройство, BAOS 838 module конфигурируется через ETS. Аппликация в данном случае используется "KNX BAOS 830", ее можно скачать с официального сайта производителя. Устройство поддерживает до 1000 групповых адресов.


Настройка Raspberry Pi


Первым делом устанавливаем raspbian-stretch-lite, настраиваем доступ по ssh.


Далее, настраиваем UART интерфейс. Для Raspberry Pi 3:


sudo sh -c "echo dtoverlay=pi3-miniuart-bt >>/boot/config.txt"

Из /boot/cmdline.txt убираем запись console=serial0,115200


Добавляем пользователя в группу dialout:


sudo usermod -a -G dialout YOURUSERNAME

Перезагружаемся


reboot

Устанавливаем nodejs, git:


curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
sudo apt-get install -y nodejs git

Подготовка закончена, переходим к нашему проекту.


bobaos-cli


Для тестирования можно использовать простой интерфейс для командной строки. Устанавливаем:


sudo npm install -g bobaos-cli

Запускаем:


% bobaos-cli
bobaos> help
Commands:

  help [command...]                   Provides help for a given command.
  exit                                Exits application.
  open [options]                      Open serial port
  getDatapointDescription [options]   GetDatapointDescription.Req service
  setDatapointValue [options]         SetDatapointValue.Req service with command "set and send to bus"
  readDatapointFromBus [options]      SetDatapointValue.Req service with command "read via bus"
  getDatapointValue [options]         GetDatapointValue.Req service
  getParameterByte [options]          GetParameterByte.Req service

Далее мы открываем порт командой open. По умолчанию открывается соединение с устройством /dev/ttyAMA0, если в системе другой интерфейс, используем опцию -p, --port.


Далее, мы можем получить информацию о сконфигурированных датапоинтах:


bobaos> getDatapointDescription -s 1 -n 10
{ service: 'GetDatapointDescription.Res',
  direction: 'response',
  error: false,
  start: 1,
  number: 10,
  payload: 
   [ { id: 1, valueType: 8, configFlags: 95, dpt: 'dpt9' },
     { id: 2, valueType: 7, configFlags: 87, dpt: 'dpt5' },
     { id: 3, valueType: 7, configFlags: 87, dpt: 'dpt5' },
     { id: 4, valueType: 7, configFlags: 87, dpt: 'dpt5' },
     { id: 5, valueType: 7, configFlags: 87, dpt: 'dpt5' },
     { id: 6, valueType: 0, configFlags: 95, dpt: 'dpt1' },
     { id: 7, valueType: 0, configFlags: 95, dpt: 'dpt1' },
     { id: 8, valueType: 0, configFlags: 87, dpt: 'dpt1' },
     { id: 9, valueType: 0, configFlags: 87, dpt: 'dpt1' },
     { id: 10, valueType: 14, configFlags: 87, dpt: 'dpt16' } ] }
bobaos> 

В моем случае первый — датчик температуры, далее идет несколько однобайтных значения, несколько однобитных, и строковое значение dpt16.


Получаем значения:


bobaos> getDatapointValue -s 1 -n 10
{ service: 'GetDatapointValue.Res',
  direction: 'response',
  error: false,
  start: 1,
  number: 10,
  payload: 
   [ { id: 1, state: 16, length: 2, value: <Buffer 0c bf> },
     { id: 2, state: 0, length: 1, value: <Buffer 00> },
     { id: 3, state: 0, length: 1, value: <Buffer 00> },
     { id: 4, state: 0, length: 1, value: <Buffer 00> },
     { id: 5, state: 0, length: 1, value: <Buffer 00> },
     { id: 6, state: 0, length: 1, value: <Buffer 00> },
     { id: 7, state: 0, length: 1, value: <Buffer 00> },
     { id: 8, state: 0, length: 1, value: <Buffer 00> },
     { id: 9, state: 0, length: 1, value: <Buffer 00> },
     { id: 10,
       state: 0,
       length: 14,
       value: <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00> } ] }
bobaos> 

Значения возвращаются в непреобразованном виде, для преобразования можно использовать библиотеку knx-dpts-baos, поддерживающую типы от dpt1 до dpt18.


Устанавливаем значение:


bobaos> setDatapointValue -s 2 -v 128 -t dpt5
{ service: 'SetDatapointValue.Res',
  direction: 'response',
  error: false,
  start: 2,
  number: 0,
  payload: null }
bobaos> getDatapointValue -s 2
{ service: 'GetDatapointValue.Res',
  direction: 'response',
  error: false,
  start: 2,
  number: 1,
  payload: [ { id: 2, state: 16, length: 1, value: <Buffer 80> } ] }
bobaos> 

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


Использование в приложениях


Устанавливаем npm пакет:


npm install --save bobaos

Добавляем в наш скрипт:


  const Baos = require('bobaos');
  const app = new Baos({serialPort: {device: '/dev/ttyAMA0'}, debug: false});
  // send requests after successful initial reset
  app.on('open', () => {
    app
      .getDatapointDescription(1, 10)
      .getParameterByte(1, 10)
      .readDatapointFromBus(1, 2) // good
      .readDatapointFromBus(1, 10) // error!
      .getDatapointValue(1, 10)
      .setDatapointValue(2, Buffer.alloc(1, 0xc0))
      .getDatapointValue(2);
  });

  // listen to incoming events and responses
  app.on('service', console.log);

Вывод должен выглядить примерно так:


{ service: 'GetParameterByte.Res',
  error: false,
  start: 1,
  number: 10,
  payload: <Buffer 01 03 05 07 09 0b 0a 00 00 00> }
{ service: 'SetDatapointValue.Res',
  error: false,
  start: 1,
  number: 0,
  payload: null }
{ service: 'GetDatapointValue.Res',
  error: false,
  start: 1,
  number: 1,
  payload: [ { id: 1, state: 4, length: 2, value: <Buffer 0c fb> } ] }
  ....
  ....

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


Ссылки


  1. Репозиторий на github
  2. KNX BAOS Module 838 kBerry. По ссылке можно посмотреть информацию, скачать аппликацию для ETS.
  3. Описание протокола
  4. Официальная документация по установке на Raspberry Pi

Комментарии (6)


  1. lingvo
    15.01.2018 18:55

    Вопрос — можно ли это все подключить к Nodered, чтобы сделать вот так: https://geektimes.ru/post/279814/
    Например приделав протокол MQTT?


    1. bobalus Автор
      16.01.2018 00:17

      Да, можно. Причем вариантов несколько.


      Первый, полагаю более сложный в разработке, в случае если node-red крутится на Raspberry Pi, можно написать плагин для node-red, который напрямую взаимодействует с шиной KNX, получает с node-red значения, отправляет их.


      Второй, как предлагаете, реализовать MQTT.


      Реализация в данном случае будет выглядеть примерно так:


      // app - наш baos
      // mqttClient - клиент MQTT
      // DPTS = require('knx-dpts-baos') - кодирует, декодирует значения для KNX
      
      mqttClient.on('connect', function () {
        client.subscribe('/baos/datapoint/1/switch')
        client.subscribe('/baos/datapoint/1/brightness')
      })
      
      mqttClient.on('message', function (topic, message)  {
          var value = parseInt(message);
          switch (topic) {
              case '/baos/datapoint/1/switch':
                  app.setDatapoint(1, DPTS.dpt1.fromJS(value);
                  break;
              case '/baos/datapoint/1/brightness':
                  app.setDatapoint(1, DPTS.dpt5.fromJS(value);
                  break;
              default:
                  break;
          }
      })


      1. lingvo
        16.01.2018 09:30

        Было бы неплохо заиметь MQTT, тогда — этакий шлюз MQTT — KNX. На форумах по домашней автоматизации — желанная штука


  1. evgeny_boger
    16.01.2018 00:23

    Существующие решения

    Ещё есть Wiren Board с KNX-модулем, одна штука выходит около 25 000р в сумме.
    И даже ETS не нужен, нужно только телеграммы туда-сюда слать из JS-движка правил.


    1. bobalus Автор
      16.01.2018 00:44

      А вот npm модуль прикрутить к JS-движку каким образом? Для примера — если задача сделать mqtt шлюз для KNX, или плагин для homebridge.

      В моем списке нету, т.к. не работал с вашим оборудованием. Но, когда искал информацию как оформить статью, наткнулся на вашу статью, оттуда же взял и раздел «Мотивация» :)
      Заинтересовался, почитал вас. Живу недалеко от Долгопрудного, если что могу заехать к вам. Но это если есть время, желание.


      1. evgeny_boger
        16.01.2018 02:21

        npm-модуль без изменений к нашему движку не прикрутить к сожалению, потому что у нас там тоже не нода и не V8, а свой сервис на интерпретаторе duktape.

        С другой стороны,
        1) Можно запустить «чистую» ноду на WB, как и на любом контроллере с Linux
        2) mqtt-шлюз для KNX — это у нас функциональность из коробки, для этого есть специальный наш сервис, wb-mqtt-knx (исходники на гитхабе)

        Заходите в гости как будет время, всё расскажем и покажем