Считывание показаний через MBUS и передача через MQTT Landis+Gyr Ultraheat UH50

Парни них хелп!
Удалось подключить к WB свой счетчик тепла (через такой адаптер RS485 to MBUS https://a.aliexpress.com/_uuHbAP )

Скомпилировал эту библиотеку rSCADA - Raditex control systems

Получил такой ответ на запрос:

root@wirenboard-ATZ522DE:~# mbus-serial-request-data /dev/ttyRS485-2 -d
<MBusData>

    <SlaveInformation>
        <Id>68924134</Id>
        <Manufacturer>LUG</Manufacturer>
        <Version>4</Version>
        <ProductName>Landis &amp; Gyr Ultraheat UH50</ProductName>
        <Medium>Heat: Outlet</Medium>
        <AccessNumber>0</AccessNumber>
        <Status>00</Status>
        <Signature>0000</Signature>
    </SlaveInformation>

    <DataRecord id="0">
        <Function>Instantaneous value</Function>
        <Unit>Actuality Duration (seconds)</Unit>
        <Value>4</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="1">
        <Function>Instantaneous value</Function>
        <Unit>Averaging Duration (seconds)</Unit>
        <Value>4</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="2">
        <Function>Instantaneous value</Function>
        <Unit>Energy (MJ)</Unit>
        <Value>34732</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="3">
        <Function>Instantaneous value</Function>
        <Unit>Volume (1e-2  m^3)</Unit>
        <Value>198075</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="4">
        <Function>Instantaneous value</Function>
        <Unit>Power (100 W)</Unit>
        <Value>34</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="5">
        <Function>Instantaneous value</Function>
        <Unit>Volume flow (m m^3/h)</Unit>
        <Value>192</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="6">
        <Function>Instantaneous value</Function>
        <Unit>Flow temperature (deg C)</Unit>
        <Value>64</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="7">
        <Function>Instantaneous value</Function>
        <Unit>Return temperature (deg C)</Unit>
        <Value>49</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="8">
        <Function>Instantaneous value</Function>
        <Unit>Temperature Difference (1e-1  deg C)</Unit>
        <Value>155</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="9">
        <Function>Instantaneous value</Function>
        <Unit>Volume (1e-2  m^3)</Unit>
        <Value>180958</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="10">
        <Function>Instantaneous value</Function>
        <Unit>Energy (MJ)</Unit>
        <Value>32051</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="11">
        <Function>Instantaneous value</Function>
        <Unit>Fabrication number</Unit>
        <Value>68924134</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="12">
        <Function>Instantaneous value</Function>
        <Unit>Averaging Duration (minutes)</Unit>
        <Value>60</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="13">
        <Function>Maximum value</Function>
        <Unit>Power (100 W)</Unit>
        <Value>62</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="14">
        <Function>Maximum value</Function>
        <Unit>Power (100 W)</Unit>
        <Value>62</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="15">
        <Function>Maximum value</Function>
        <Unit>Volume flow (m m^3/h)</Unit>
        <Value>264</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="16">
        <Function>Maximum value</Function>
        <Unit>Flow temperature (deg C)</Unit>
        <Value>149</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="17">
        <Function>Maximum value</Function>
        <Unit>Return temperature (deg C)</Unit>
        <Value>74</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="18">
        <Function>Instantaneous value</Function>
        <Unit>On time (hours)</Unit>
        <Value>37367</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="19">
        <Function>Value during error state</Function>
        <Unit>On time (hours)</Unit>
        <Value>9522</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="20">
        <Function>Value during error state</Function>
        <Unit>On time (hours)</Unit>
        <Value>9522</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="21">
        <Function>Instantaneous value</Function>
        <Unit>Time Point (date)</Unit>
        <Value>2000-01-01</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="22">
        <Function>Instantaneous value</Function>
        <Unit>Energy (kWh)</Unit>
        <Value>0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="23">
        <Function>Instantaneous value</Function>
        <Unit>Energy (kWh)</Unit>
        <Value>0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="24">
        <Function>Instantaneous value</Function>
        <Unit>Energy (kWh)</Unit>
        <Value>0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="25">
        <Function>Instantaneous value</Function>
        <Unit>Energy (kWh)</Unit>
        <Value>0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="26">
        <Function>Instantaneous value</Function>
        <Unit>Energy (kWh)</Unit>
        <Value>0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="27">
        <Function>Instantaneous value</Function>
        <Unit>Energy (kWh)</Unit>
        <Value>0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="28">
        <Function>Maximum value</Function>
        <Unit>Flow temperature (deg C)</Unit>
        <Value>75</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="29">
        <Function>Maximum value</Function>
        <Unit>Return temperature (deg C)</Unit>
        <Value>74</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="30">
        <Function>Maximum value</Function>
        <Unit>Volume flow (m m^3/h)</Unit>
        <Value>192</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="31">
        <Function>Maximum value</Function>
        <Unit>Power (100 W)</Unit>
        <Value>47</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="32">
        <Function>Value during error state</Function>
        <Unit>On time (hours)</Unit>
        <Value>9522</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="33">
        <Function>Instantaneous value</Function>
        <Unit>Energy (MJ)</Unit>
        <Value>34199</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="34">
        <Function>Instantaneous value</Function>
        <Unit>Energy (kWh)</Unit>
        <Value>0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="35">
        <Function>Instantaneous value</Function>
        <Unit>Energy (kWh)</Unit>
        <Value>0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="36">
        <Function>Instantaneous value</Function>
        <Unit>Energy (kWh)</Unit>
        <Value>0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="37">
        <Function>Instantaneous value</Function>
        <Unit>Volume (1e-2  m^3)</Unit>
        <Value>194644</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="38">
        <Function>Instantaneous value</Function>
        <Unit>Time Point (time &amp; date)</Unit>
        <Value>2021-02-08T16:09:00</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="39">
        <Function>Manufacturer specific</Function>
        <Value>21 04 00 10 A0</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

</MBusData>

Из этого массива мне нужны только эти:

 <DataRecord id="2">
        <Function>Instantaneous value</Function>
        <Unit>Energy (MJ)</Unit>
        <Value>34732</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

<DataRecord id="4">
        <Function>Instantaneous value</Function>
        <Unit>Power (100 W)</Unit>
        <Value>34</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="5">
        <Function>Instantaneous value</Function>
        <Unit>Volume flow (m m^3/h)</Unit>
        <Value>192</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="6">
        <Function>Instantaneous value</Function>
        <Unit>Flow temperature (deg C)</Unit>
        <Value>64</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="7">
        <Function>Instantaneous value</Function>
        <Unit>Return temperature (deg C)</Unit>
        <Value>49</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

    <DataRecord id="8">
        <Function>Instantaneous value</Function>
        <Unit>Temperature Difference (1e-1  deg C)</Unit>
        <Value>155</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

  <DataRecord id="11">
        <Function>Instantaneous value</Function>
        <Unit>Fabrication number</Unit>
        <Value>68924134</Value>
        <Timestamp>2021-02-08T18:34:01</Timestamp>
    </DataRecord>

Как из этого всего можно сделать счетчик в GUI WB аналогичный вашему?

https://wirenboard.com/wiki/images/7/71/WB-MAP3H_webui_devices.png

Тут надо с одной стороны создать в MQTT “структуру” под данные. Делается это например виртуальным устройством wb-rules. И из того же скрипта запускать скомпилированную программ, парсить ее вывод и записывать в поля. Что подсказать?

А что за счётчик тепла у вас?

Так получается? Или в чем-нибудь помочь?

А вдруг у вас есть пример такого решения?

Вот такой

Именно такого - нет, но могу написать “шаблон” скрипта, который можно использовать.

1 лайк

Я был бы вам категорично признателен и готов сделать донат в удобной вам форме )

Донат - не надо, общим делом заняты :wink:
Но сегодня не успеваю. Завтра напишу.

1 лайк

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

//02_15_test_02.js
 
defineVirtualDevice("mbus", {
  title: "Mbus OUT", //
  cells: {
    Energy : {
        type : "text",
        value : "",
        readonly: false,
    },
    Power : {
        type : "text",
        value : "",
        readonly: false,
    },
  }
});


//var oParser = new DOMParser();



log.info ("START 02_15_test_02.js")
// Запуск shell-команды с захватом вывода
    runShellCommand("ls /etc", //сюда и записываем "mbus-serial-request-data /dev/ttyRS485-2 -d"
        {
      captureOutput: true, //Захватывать stdout
      captureErrorOutput: true, //Захватывать stderr (если нужно)
      exitCallback: function (exitCode, capturedOutput, capturedErrorOutput) //Функция, в которую попадает вывод
            {
                log("cmd output: " + capturedOutput); //строка полностью
                capturedArray = capturedOutput.split("\n"); //Разбиваем полученную строку по символу переноса строки.
                capturedArray.forEach(function(item, index){ //Для каждой строки
                  var tmpstring = item.trim(); //порежем пробелы
                  switch(tmpstring) {
                    case "<Unit>Energy (MJ)</Unit>": //Одно из условий
                      dev["mbus"]["Energy"] = capturedArray[index+1] //Выводим СЛЕДУЮЩУЮ строку в поле. ну и обработать как надо, если надо.
                    case "<Unit>Power (100 W)</Unit>": //Одно из условий
                      //log.info("index", index)
                      dev["mbus"]["Power"] = capturedArray[index+1] //ну и обработать как надо
                      //log.info(capturedArray[index+1])
                  }
                })
                log("cmd Erroroutput: " + capturedErrorOutput); //Если нужен вывод  stderr
            }
         })

Вот пример как раз нужного. Упаковать запуск внешнего приложения и парсинг в функцию - и вызывать по таймеру.

1 лайк

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

Meter.js
var MBUS_SERIAL = '/usr/local/bin/mbus-serial-request-data'
var XMLLINTER = '/usr/bin/xmlstarlet'
var DEVICE = '/dev/ttyRS485-2'

var METRICS = {
    2: {
        name: 'Energy',
        multiplier: 0.000239,
    },
    4: {
        name: 'CurrentPower',
        multiplier: 100
    },
    5: {
        name: 'VolumeFlow',
        multiplier: 0.001
    },
    6: {
        name: 'FlowTemperature',
        multiplier: 1
    },
    7: {
        name: 'ReturnTemperature',
        multiplier: 1
    },
    8: {
        name: 'TemperatureDifference',
        multiplier: 0.1
    },
    11: {
        name: 'FabricationNumber',
        multiplier: 1
    }
}

defineVirtualDevice("ultraheat", {
    title: "Ultraheat T550 (UH50)",
    cells: {
        Energy: {
            type: "heat_energy",
            value: 0
        },
        CurrentPower: {
            type: "power",
            value: 0
        },
        VolumeFlow: {
            type: "water_flow",
            value: 0
        },
        FlowTemperature: {
            type: "temperature",
            value: 0
        },
        ReturnTemperature: {
            type: "temperature",
            value: 0
        },
        FabricationNumber: {
            type: "value",
            value: 0
        },
        TemperatureDifference: {
            type: "temperature",
            value: 0
        }
    }
});

defineRule('ultraheat_cron', {
    when: cron('@every 600s'), // interval
    then: function () {
        var mbusCmd = MBUS_SERIAL + " " + DEVICE + " -d | " + XMLLINTER + " sel -T -t -v '/MBusData/DataRecord/Value' -n";
     runShellCommand(mbusCmd, {
            captureOutput: true,
            exitCallback: function (exitCode, capturedOutput) {
                // log.info('ultraheat: var captureOutput: ' + capturedOutput);

                capturedArray = capturedOutput.split("\n");
                for (var id in METRICS) {
                    var metricName = METRICS[id]['name'];
                    var metricValue = capturedArray[id];
                    dev.ultraheat[metricName] = Number(metricValue) * METRICS[id]['multiplier'];
                    // log.info('ultraheat: var captureOutput metric[' + metricName + ']: ' + metricValue);
                }
            }
        });
    }
});

1 лайк

Товарищу - респект, очень элегантно распарсил вывод, решение намного красивее чем у меня.

1 лайк

Получил вот такой адаптер MBUS - USB
Подправил в конфиге - все работает!

Покупал здесь:

1 лайк

Отлично!
И, если можно - выложите сюда же инструкции по сбрке самого софта.

Проделал такие же приседания, оставлю тут для потомков.

В общем я по самой первой ссылке заказал себе mbus->rs485 на дин рейку.

Чтобы скомпилировать libmbus пришлось поставить себе виртуалку ubuntu arm64 18.04.6 тк более в более новых версиях используется libc6 более новая чем в wirenboard.
Ubuntu не захотела ставиться на виртуалку, не могла примоунтить cdrom, воспользовался mini.iso установщиком и установил из инета.

Чтобы скомпилировать libmbus, нужно в виртуалку доставить

sudo apt install git devscripts autoconf libtool dbhelper

склонировать репо GitHub - rscada/libmbus: Meter-bus library and utility programs и внутри него вызвать ./build-deb.sh, если все скомпилиться, в папке выше будут лежать debian пакеты, у меня это libmbus1_0.9.0_arm64.deb
Копируем этот пакет на wirenboard (scp) и через apt instal ./libmbus1_0.9.0_arm64.deb устанавливаем в систему.

в папке /usr/bin появятся mbus-serial-* утилиты
Подключение к mbus можно выполнить через mbus-serial-request-data -d -b 2400 /dev/ttyRS485-1 1 , мой счетчик пульсар имеет адрес 1, общается на скорости 2400 и подключен к 1 RS485 порту.
Если все ОК, то в ответ придет xml похожий выше.

Если кому лень это делать, приаттачил получившийся у меня пакет с libmbus
libmbus1_0.9.0_arm64.deb (104,3 КБ)

1 лайк

как собрать под свой контроллер ?
у меня Wiren Board 7.2.1
при попытке поставить пакет пишет:
The following packages have unmet dependencies:
libmbus1:arm64 : Depends: libc6:arm64 (>= 2.17) but it is not installable

мне надо еще более древний вариант ?

собрал в окружении WB

инструкций не нашел пишу свою

делал в виртуалке ubuntu 22.04 на ProxMox

ставим докер
sh <(curl -sSL https://get.docker.com) или по офф инструкции докера.
и
apt-get install qemu-user-static

ставим окружение wb
не из под рута (создать пользователя с одинаковым UID GID делать от пользователя через SUDO)
от него запускать wbdev

wget https://raw.githubusercontent.com/wirenboard/wirenboard/master/wbdev
chmod +x wbdev

запускаем ./wbdev (весит 9гб! проверьте есть ли место перед запуском ~12гб)

после установки WBDEV
должен появится образ в докере. Посмотреть образы команда

docker images

скачиваем исходники
git clone GitHub - rscada/libmbus: Meter-bus library and utility programs

cd libmbus
создать внутри папку м4 (без нее ругается)

mkdir m4

находясь внутри каталога libmbus запустить сборку пакетов команда cdeb путь до wbdev (пример sudo /home/user/wbdev cdeb)

в каталоге /home/user должны появится .deb пакеты.
посмотреть
cd …
ls -al

копируем пакет на контроллер
scp ./libmbus1_0.9.0_armhf.deb root@192.168.5.250:/root/

мой пакет, если лень собирать.
libmbus1_0.9.0_armhf.deb (104,3 КБ)

подправил скрипт под свой счетчик ПУЛЬС СТА-15-М-Rs-485

для его работы требуется xmlstarlet
установка - apt install xmlstarlet

проверьте пути до бананников/
в var DEVICE укажите порт и скорость, а так же адрес счетчика.
я использовал secondary-ID.
что бы найти вторичный адрес -
mbus-serial-scan-secondary -d -b 2400 /dev/ttyRS485-2 24015622FFFFFFFF

24015622 - это SN счетчика

Pulse.js (2,4 КБ)

А адаптер для этого счетчика не нужен? цепляем напрямую?

адаптер не нужен если счетчик (ПУЛЬС СТА-15-М-Rs-485) с интерфейсом RS-485 указан в названии счетчика. уже внутри этого интерфейса используется протокол modbus, поэтому надо отдельный порт, на контроллере и либа для работы по протоколу m-busдля него. А так есть версия этого же счетчика ПУЛЬС Ста-15 M-Bus, и тогда надо будет адаптер m-bus.