Драйвер wb-mqtt-serial: шаблоны

Материал из Wiren Board

Описание

Использование шаблонов позволяет один раз описать устройство и использовать созданное описание для любого числа устройств на одном или нескольких serial-портах. Если вы внесли изменения в шаблоны и используете их для настройки устройства в веб-интерфейсе — дважды обновите страницу настроек с очисткой кэша браузера. В Google Chrome, используйте комбинацию клавиш Ctrl+Shift+R. Предустановленные шаблоны находятся в папке /usr/share/wb-mqtt-serial/templates.

Пользовательские шаблоны для wb-mqtt-serial рекомендуем хранить в папке /etc/wb-mqtt-serial.conf.d/templates. В каждом файле, размещенном в папке templates находится описание одного типа устройств. Пользовательские шаблоны имеют более высокий приоритет над предустановленными: при одинаковом имени устройства в пользовательском и предустановленном шаблонах, будет использован пользовательский.

Шаблоны удобно использовать для настройки драйвера в веб-интерфейсе.

Подготовка

Перед написанием шаблона рекомендуем проверить правильность подключения с помощью прямого обмена данными с устройством.

Подключите устройство к контроллеру и прочитайте значение одного из его modbus-регистров. Описание адресов modbus-регистров и параметры подключения можно найти в документации на подключаемое устройство, а для чтения информации из устройства можно использовать утилиту modbus_client.

Базовая структура шаблона

{ 
{ 
"device": {
	// параметры устройства
    "параметр":"значение", 
    "параметр":"значение", 
    ...

    "setup": [
        // секция инициализации устройства
		"параметр": "значение",
		...	
    ],
	"parameters": {
		// секция пользовательской инициализации устройства
	    "Param1Name": { // имя параметра
	        "элемент_параметра": "значение",
	        ...       
	        "элемент_параметра": "значение"
	    },
	    "Param2Name": {        
	        ...               
	    }
	},
    "channels": [ 
        {
			// описание канала 1
			"параметр": "значение",
			...				
        }, 
        {
			// описание канала 2
			"параметр": "значение",
			...		
        },
         ...
        ]
    }
}

В наших шаблонах мы используем отступы по 4 пробела на уровень, что обеспечивает хорошую читаемость кода. Новые шаблоны мы рекомендуем создавать с такими же отступами.

Параметры устройства (device)

В начале шаблона задаются параметры устройства в виде набора пар "ключ":"значение".

  • title — название устройства в выпадающем списке на странице конфигурации.
  • device_type — тип устройства, строка. По полю device_type драйвер будет искать необходимый шаблон конфигурации устройства в папках с шаблонами.
  • name — отображаемое имя устройства. Публикуется как .../meta/name в MQTT и отображается в заголовке окна на странице Devices.
  • id — уникальный идентификатор устройства в MQTT. Каждый элемент в devices должен иметь уникальный id. Если не задан, то используется параметр name.
  • slave_id — идентификатор устройства (адрес). Если не задан — используется широковещательный запрос.
  • enabled — включает или выключает опрос устройства. Доступные значения: true, false. По умолчанию — true.
  • protocol — протокол передачи данных, если не указан — используется modbus. Возможные варианты:
    • modbus,
    • uniel,
    • milur,
    • mercury230,
    • ivtm,
    • s2k,
    • modbus_io,
    • pulsar,
    • mercury200,
    • lls,
    • neva,
    • energomera_iec.
  • max_reg_hole — максимальное количество считываемых «пустых» регистров. Параметр поддерживается только modbus-устройствами.
  • max_bit_hole — аналог max_reg_hole для битовых регистров: coils и discrete inputs.
  • max_read_registers — максимальное количество регистров в одной пакетной операции чтения.
  • poll_interval — минимальный интервал опроса регистров устройства в миллисекундах.
  • response_timeout_ms — максимальное время ожидания ответа от устройства в миллисекундах. Если в течение этого времени ответ не будет получен — драйвер продолжит опрос других регистров и устройств.
  • frame_timeout_ms — минимально необходимая задержка между посылками в миллисекундах. То есть время между последним принятым пакетом данных и новым запросом к устройству. При использовании протокола Modbus применяется только перед первым запросом к каждому следующему в цикле опроса устройству.
  • guard_interval_us — дополнительная задержка перед каждой отправкой данных в порт в микросекундах. Если при работе с устройством теряются пакеты — попробуйте увеличить значение этого параметра. Для соответствия протоколу Modbus RTU, установите этот параметр в значение не менее 3.5 символа при выбранной скорости — это не нужно для устройств Wiren Board, но может потребоваться для устройств сторонних производителей. Нужное значение рассчитывается по формуле: guard_interval_us = (3.5*11*106)/(скорость в бит/с). Например, для скорости 9600 бит/с guard_interval_us = (3.5*11*106)/9600 = 4000 мкс.
  • device_timeout_ms — интервал, по истечении которого (а также device_max_fail_cycles) устройство будет помечено отключенным и будет опрашиваться в ограниченном режиме.
  • device_max_fail_cycles — количество неудачных циклов опроса устройства.
  • password — пароль для доступа к устройству, массив байт.
  • access_level — уровень доступа при опросе устройства.
  • channels — список каналов устройств, задается в виде массива [...], каждый из элементов которого — это описание отдельного канала. Подробнее читайте ниже.

Обязательно нужно указать параметры device_type и device. Остальные параметры можно опустить, в этом случае будут подставлены значения по умолчанию.

Изменять параметры устройства можно и в веб-интерфейсе, для этого перейдите в настройки serial-портов, найдите нужное устройство и откройте свойства объекта DeviceProperties.

Каналы устройства (channels)

Структура:

"channels": [
        {
            "name": "имя канала 1",
            ...
        },
        {
            "name": "имя канала 2",
            ...
        },
        ...
    ]

Структура содержит набор полей:

  • name — уникальное для устройства имя канала.
  • id — имя MQTT контрола для канала, публикуется как /devices/<идентификатор устройства>/controls/<ID канала> Если не задан, то используется значение name.
  • enabled — включает или выключает опрос канала. Доступные значения: true, false. По умолчанию — true.
  • reg_type — тип modbus-регистра. Возможные значения:
    • coil — 1 бит, чтение/запись;
    • discrete — 1 бит, только чтение;
    • holding — 16 бит, чтение/запись, код функции на запись выбирается автоматически, в зависимости от размера;
    • holding_single — то же что и holding однако регистры записываются всегда по одному, кодом 06;
    • holding_multi — то же что и holding однако регистры записываются всегда кодом 16;
    • input — 16 бит, только чтение.
  • address — адрес регистра. Можно читать отдельные биты регистра, для этого запишите адрес в формате: "address":"reg:shift:width", где reg — адрес регистра, shift — смещение от начала, а width — количество считываемых битов. Например, "address":"109:1:2" — прочитать второй и третий биты регистра, расположенного по адресу 109.
  • type — тип контрола: виртуального элемента для представления данных в веб-интерфейсе контроллера.
  • format — тип переменной, описывающей значение.
  • word_order — порядок 16-битных слов для каналов, имеющих размер больше 16 бит. Доступные значения:
    • big_endian — [0xAA 0xBB] [0xCC 0xDD] => 0xAABBCCDD. Значение по умолчанию.
    • little_endian — [0xAA 0xBB] [0xCC 0xDD] => 0xCCDDAABB.
  • poll_interval — минимальный интервал опроса регистра в миллисекундах.
  • unsupported_value — значение, получаемое при последовательном чтении диапазона регистров, если устройство не поддерживает запрашиваемый регистр. Используется некоторыми протоколами для определения доступности регистров устройства.
  • max — максимальное значение регистра, используется для построения представления устройства в веб-интерфейсе.
  • scale — коэффициент, на который умножается значение регистра перед публикацией в MQTT.
  • offset — значение, которое прибавляется к значению регистра перед публикацией в MQTT.
  • round_to — порядок, до которого будет округляться значение после всех преобразований. Например, 0.1 — округлять до десятых.
  • readonly — установка флага запрещает изменение значения в веб-интерфейсе или по MQTT. Возможные значения: true, false. По умолчанию — false.
  • on_value — значение, которое будет записано в регистр, при записи единицы в mqtt-топик канала.
  • error_value — полученное из устройства значение регистра, обозначающее ошибку. Используется в веб-интерфейсе для подсвечивания параметра красным цветом в случае ошибки.

Обязательно нужно указать поля name, reg_type, address и type. Остальные параметры можно опустить, в этом случае будут подставлены значения по умолчанию.

Доступные типы контролов (type)

  • atmospheric_pressure — атмосферное давление, millibar (100 Pa).
  • concentration — концентрация, ppm.
  • current — ток, отображается как value.
  • dimmer — диммер, отображается как value.
  • heat_energy — тепловая энергия, Gcal.
  • heat_power — расход тепловой энергии, Gcal/h.
  • lux — освещенность.
  • power — мощность, W.
  • power_consumption — энергопотребление, kWh.
  • pressure — давление, bar
  • pushbutton — кнопка.
  • rainfall — осадки, mm/h.
  • range — ползунок.
  • rel_humidity — влажность, %, RH.
  • resistance — сопротивление, Ohm.
  • rgb — rgb.
  • sound_level — уровень звука.
  • switch — переключатель положений ON/OFF.
  • temperature — температура, °C.
  • text — текстовая строка.
  • value — неформатированное значение.
  • voltage — напряжение, V.
  • water_consumption — потребление воды, m3.
  • water_flow — поток воды m3/h
  • wind_speed — скорость ветра, m/s.
  • wo-switch — переключатель положений ON/OFF.

Доступные типы переменных (format)

  • s16 — Signed 16-bit integer.
  • u16 — Unsigned 16-bit integer, используется по умолчанию.
  • s8 — Signed 8-bit integer.
  • u8 — Unsigned 8-bit integer.
  • s24 — Signed 24-bit integer.
  • u24 — Unsigned 24-bit integer.
  • s32 — Signed 32-bit integer.
  • u32 — Unsigned 32-bit integer.
  • s64 — Signed 64-bit integer.
  • u64 — Unsigned 64-bit integer.
  • bcd8 — 8-bit BCD.
  • bcd16 — 16-bit BCD.
  • bcd24 — 24-bit BCD.
  • bcd32 — 32-bit BCD.
  • float — IEEE754 32-bit float.
  • double — IEEE754 64-bit float (double).
  • char8 — 8-bit ASCII char.

Секция инициализации (setup-секция)

Секция содержит перечень параметров, которые передаются устройству при перезапуске дайвера wb-mqtt-serial или при восстановлении связи с устройством. С ее помощью удобно конфигурировать устройства: для работы в каком-то определенном режиме; задавать изначальные значения яркости, положения сервоприводов, состояния реле или режима работы с входами релейных модулей и т. п.

Важно! Изменение параметров, описанных в setup-секции с помощью веб-интерфейса не предусмотрено. Если вы хотите изменять параметры инициализации в веб-интерфейсе, используйте parameters-секцию.

В секции доступны элементы:

  • title — наименование параметра, будет отображен в веб-интерфейсе.
  • address — адрес регистра, в который нужно записать параметр.
  • value — фиксированное значение. Если у регистра указать фиксированное значение, то пользователь не сможет его изменить через веб-интерфейс.

Значения address и value можно указывать в десятичной или шестнадцатеричной системе счисления.

Пример setup-секции:

"setup": [
    {
        "title": "s12",
        "address": 20000,
        "value": "0xfff2"
    }, 
    {
        "title": "s45",
        "address": 24010,
        "value": "0x0003"
    }   

]

Секция параметров (parameters-секция)

Представление parameters-секции в веб-интерфейсе — Device options

Секция содержит перечень параметров, которые передаются устройству при перезапуске дайвера wb-mqtt-serial или при восстановлении связи с устройством.

В шаблоне можно перечислить параметры и задать их значения по умолчанию.

Назначение параметров аналогично setup-секции, но есть отличия в использовании:

  • Пользователь сможет изменить значения параметров в веб-интерфейсе.
  • Можно разрешить или запретить отправку параметра в устройство. Для этого установите или снимите флажок напротив названия параметра.
  • Секция имеет другую структуру:
    "parameters": {
        "Param1Name": { // имя параметра
            "элемент_параметра": "значение",
            ...       
            "элемент_параметра": "значение"
        },
        "Param2Name": {        
            ...               
        }
    }
    

Важно! Чтение параметров из устройства не предусмотрено.

В секции доступны элементы:

  • title — наименование параметра, будет отображен в веб-интерфейсе.
  • address — адрес регистра, в который нужно записать параметр.
  • reg_type — тип регистра: input, holding и т.п.
  • format — формат записываемого в регистр значения: s16, u32 и т.п. Все доступные форматы.
  • order — порядок сортировки.
  • scale — коэффициент, на который умножается значение перед записью в регистр.
  • offset — число, которое нужно вычесть из значения.
  • enum — массив числовых значений, которые можно записать в регистр.
  • enum_titles — массив текстовых описаний значений, указанных в enum.
  • max — максимально возможное значение.
  • min — минимально возможное значение.
  • default — значение по умолчанию. Записывается в регистр, если пользователь не выбрал свое.
  • value — фиксированное значение. Если у регистра указать фиксированное значение, то пользователь не сможет его изменить через веб-интерфейс.
  • description — пояснительный текст, который выводится рядом с полем параметра в веб-интерфейсе.

Значения address, enum, value и default можно указывать в десятичной или шестнадцатеричной системе счисления.

Записываемое в регистр значение вычисляется по формуле: (param_value - offset)/scale, где param_value — введённое пользователем значение Пример parameters-секции:

"parameters": {
    "SoundValue": {
        "title": "Sound value",
        "address": 0x00,
        "min": 0,
        "max": 100,
        "default": 0x0a
    },
    "FanMode": {
        "title": "Fan mode",
        "address": 3,
        "enum": [ 0, 1, 2, 4 ],          
        "enum_titles": [ 
          "off", 
          "slow", 
          "middle", 
          "fast"
        ],               
        "default": 1
    }
}

Вложенные устройства (subdevices)

Представление вложенных устройств в веб-интерфейсе: каналы устройства WB-MAI11

В секции subdevices можно описать вложенные устройства, которые могут иметь свои setup-секции и каналы.

Для каждого канала можно указать, что это вложенное устройство и задать список вложенных устройств, которые поддерживает канал.

Адреса регистров вложенных устройств рассчитываются как base + shift + address + stride * register_size, где:

  • base — адрес начала секции регистров родительского канала;
  • shift — смещение относительно родительского канала;
  • address — адрес, заданный в описании регистра;
  • stride — номер шага, заданный в настройках канала;
  • register_size — размер регистра.

Пример описания вложенных устройств:

{
    // Название типа устройства, оно указывается в поле device_type в файле настроек
    "device_type": "Device type name",

    // Название типа устройства, которое будет отображаться в веб-интерфейсе.
    // Необязательный параметр. Если не указан, используется device_type.
    "title": "Device",

    "device": {
        // отображаемое имя устройства. Публикуется как
        // .../meta/name в MQTT
        "name": "New device",
        
        // Остальные параметры устройства описанные ранее, кроме slave_id
        ...

        // список каналов устройства
        "channels": [
            // Пример канала, описывающий данные одного регистра
            {
                "name": "Temperature",
                "reg_type": "input",
                "format": "s32",
                "address": "0x0504"
            },

            // Канал - вложенное устройство
            {
                "name": "Sub",
                "device_type": "subdevice1"
            },

            // Канал - вложенное устройство.
            // В интерфейсе веб-интерфейса будет предложен выбор конкретного типа
            {
                "name": "Sub2",
                
                // Список типов вложенных устройств, которые можно выбрать для канала
                "oneOf": [ "subdevice1", "subdevice2" ],

                // Устройство, выбранное по умолчанию
                "device_type": "subdevice1",

                // Смещение регистров канала относительно родительского канала
                "shift": 100,

                // Номер шага регистров
                "stride": 1
            }
        ],
        // Список вложенных устройств
        "subdevices": [
            {
                // Имя вложенного устройства,
                // которое будет использоваться в поле device_type канала
                "device_type": "subdevice1",

                // Название вложенного устройства,
                // которое будет отображаться в онлайн-конфигураторе.
                // Необязательный параметр. Если не указан, используется device_type.
                "title": "Sub1",

                "device": {
                    "channels": [
                        // Вложенные устройства могут иметь свои каналы
                        {
                            "name": "c11",
                            "reg_type": "input",
                            "format": "s32",
                            "address": "0x0501"
                        },

                        // Вложенные устройства также могут иметь
                        // каналы с вложенными устройствами
                        {
                            "name": "c2",
                            "device_type": "subdevice2"
                        }
                    ]
                }
            },
            {
                "device_type": "subdevice2",
                "device": {
                    // Вложенные устройства могут иметь
                    // собственную секцию с настройками
                    "setup": [
                        {
                            "title": "s12",
                            "address": 20000,
                            "value": "0xfff2"
                        },

                        // Если конкретное значение настройки не задано, то его можно
                        // задать в файле конфигурации или через веб-интерфейс
                        {
                            "title": "s22",
                            "address": 9992,

                            // Список возможных значений
                            "enum": [1, 2, 3],

                            // Надписи в списке выбора в веб-интерфейсе
                            "enum_titles": ["one", "two", "three" ],

                            // Минимально возможное значение
                            "min": 1,

                            // Максимально возможное значение
                            "max": 3
                        }
                    ],
                    "channels": [
                        {
                            "name": "c2",
                            "device_type": "subdevice2"
                        }
                    ]
                }
            }
        ]
    }
}

Вложенные устройства рекомендуется использовать только в тех случаях, когда в зависимости от настройки меняется структура каналов или параметров. Использование вложенных устройств влияет на структуру конфигурационного файла. Для организации удобного представления настроек в web-конфигураторе без изменения структуры конфигурационного файла рекомендуется использовать группировку, описанную в следующем разделе.

Группировка каналов и параметров

Пример группировки в настройках датчика WB-MSW v.3 — каналы и параметры сгруппированы по функциям.

Каналы и параметры в шаблоне устройств могут быть объединены в группы. Группы используются для удобной организации веб-интерфейса и не влияют на структуру конфигурационного файла.

Пример группировки:

{
    "device_type": "Example",
    "device": {
        "name": "Example device",
        "channels": [
            {
                "name": "Temperature",
                "reg_type": "holding",
                "address": 1,

                // Идентификатор группы
                "group": "group1"
            },
            {
                "name": "Pressure",
                "reg_type": "holding",
                "address": 2,

                // Идентификатор группы
                "group": "group1"
            },
            {
                "name": "Uptime",
                "reg_type": "holding",
                "address": 3
            }
        ],
        "parameters": {
            "timeout": {
                "title": "Timeout",
                "address": 9992,

                // Идентификатор группы
                "group": "group1"
            },
            "reaction": {
                "title": "Reaction",
                "address": 9993
            }
        },

        // Список групп
        "groups": [
            // Описание группы
            {
                // Уникальное имя группы, отображается в web-интерфейсе
                "title": "Group 1",

                // Идентификатор группы
                "id": "group1",

                // Позиция группы в списке каналов
                // Если не задана, группа будет расположена перед остальными каналами
                "order": 3
            }
        ]
    }
}

Примеры шаблонов

Хорошим источником примеров могут служить предустановленные шаблоны драйвера wb-mqtt-serial.

Ниже мы рассмотрим несколько примеров подробнее.

WB-MSW3

Датчик WB-MSW3 в базовой комплектации может измерять температуру и влажность. Еще мы можем получить из датчика величину напряжения питания, серийный номер и управлять встроенной пищалкой.

Пример шаблона:

{
    "device_type": "WB-MSW v.3 (base)",
    "device": {
        "name": "WB-MSW v.3",
        "id": "wb-msw-v3",
        "max_read_registers": 0,
        "response_timeout_ms": 1,
        "frame_timeout_ms": 12,
        "channels": [
            {
                "name": "Temperature",
                "reg_type": "input",
                "address": 0,
                "type": "temperature",
                "format": "s16",
                "scale": 0.1,
                "error_value": "0x7FFF"
            },
            {
                "name": "Humidity",
                "reg_type": "input",
                "address": 1,
                "type": "rel_humidity",
                "scale": 0.1,
                "error_value": "0xFFFF"
            },
            {
                "name": "Input Voltage",
                "reg_type": "input",
                "address": 121,
                "scale": 0.001,
                "type": "voltage"
            },
            {
                "name": "Serial",
                "type": "text",
                "reg_type": "input",
                "address": 270,
                "format": "u32"
            },
            {
                "name": "Buzzer",
                "type": "switch",
                "reg_type": "coil",
                "address": 0
            }
        ]
    }
}

Описание параметров смотрите в описании базовой структуры шаблона.

WB-MRGBW-D

Устройство WB-MRGBW-D — четырехканальный диммер для управления светодиодными лентами. Может управлять лентой RGB+W либо независимо четырьмя одноцветными лентами. Настройки яркости хранятся в holding-регистрах. Шаблон описывает, какие регистры можно менять для получения нужной яркости каналов, отслеживать нажатия на кнопки диммера и получать значения количества нажатий.

{
    "device_type": "WB-MRGBW-D", 
    "device": {
        "name": "WB-MRGBW-D", 
        "id": "wb-mrgbw-d", 
        "max_read_registers": 0, 
        "channels": [
            {
                "name": "RGB", 
                "type": "rgb", 
                "consists_of": [
                    {
                        "reg_type": "holding", 
                        "address": 1
                    }, 
                    {
                        "reg_type": "holding", 
                        "address": 0
                    }, 
                    {
                        "reg_type": "holding", 
                        "address": 2
                    }
                ]
            }, 
            {
                "name": "White", 
                "reg_type": "holding", 
                "address": 3, 
                "type": "range", 
                "max": 255
            }, 
            {
                "name": "Button 1", 
                "reg_type": "holding", 
                "readonly": true, 
                "address": 6, 
                "type": "switch"
            }, 
            {
                "name": "Button 2", 
                "reg_type": "holding", 
                "readonly": true, 
                "address": 7, 
                "type": "switch"
            }, 
            {
                "name": "Button 3", 
                "reg_type": "holding", 
                "readonly": true, 
                "address": 8, 
                "type": "switch"
            }, 
            {
                "name": "Button 1 counter", 
                "reg_type": "holding", 
                "readonly": true, 
                "address": 32, 
                "type": "value"
            }, 
            {
                "name": "Button 2 counter", 
                "reg_type": "holding", 
                "readonly": true, 
                "address": 33, 
                "type": "value"
            }, 
            {
                "name": "Button 3 counter", 
                "reg_type": "holding", 
                "readonly": true, 
                "address": 34, 
                "type": "value"
            }, 
            {
                "name": "Serial NO", 
                "type": "text", 
                "reg_type": "holding", 
                "address": 270, 
                "format": "u32"
            }
        ]
    }
}

В этом шаблоне составной канал RGB задает одновременно яркости трех цветовых компонентов ленты, хранящихся в трех holding-регистрах и описывается следующим образом:

    {
        "name": "RGB", 
        "type": "rgb", 
        "consists_of": [
            {
                "reg_type": "holding", 
                "address": 1
            }, 
            {
                "reg_type": "holding", 
                "address": 0
            }, 
            {
                "reg_type": "holding", 
                "address": 2
            }
        ]
    },

Канал белого цвета управляется отдельно,

    {
        "name": "White", 
        "reg_type": "holding", 
        "address": 3, 
        "type": "range", 
        "max": 255
    },

Здесь указано максимальное значение, которое можно установить для ползунка яркости белого — 255:

"max": 255

Серийный номер устройства (канал «Serial NO») считывается как беззнаковое 32-битное целое:

"format": "u32"

WBIO-DO-R10A-8

Устройство WBIO-DO-R10A-8 — релейный боковой модуль, который подключается непосредственно к WBIO-разъему контроллера, и не управляется по Modbus. Но при подключении его через устройство расширения WB-MIO или WB-MIO-E становится полноценным modbus-устройством, для которого есть соответствующий шаблон.

{
    "device_type": "WBIO-DO-R10A-8",
    "device": {
        "name": "WBIO-DO-R10A-8",
        "id": "wb-mio-gpio",
        "protocol": "modbus_io",
        "stride": 1000,
        "shift": 500,
        "max_read_registers": 0,
        "setup": [
            {
                "title": "IODIR",
                "address": 10000,
                "value": "0x0000"
            },
            {
                "title": "IPOL",
                "address": 10001,
                "value": "0x0000"
            },
            {
                "title": "GPINTEN",
                "address": 10002,
                "value": "0xffff"
            },
            {
                "title": "DEFVAL",
                "address": 10003,
                "value": "0x0000"
            },
            {
                "title": "INTCON",
                "address": 10004,
                "value": "0x0000"
            },
            {
                "title": "IOCON",
                "address": 10005,
                "value": "0x4444"
            },
            {
                "title": "FLAG",
                "address": 9999,
                "value": 1
            }
        ],
        "channels": [
            {
                "name": "K1",
                "reg_type": "coil",
                "address": 0,
                "type": "switch"
            },
            {
                "name": "K2",
                "reg_type": "coil",
                "address": 1,
                "type": "switch"
            },
            {
                "name": "K3",
                "reg_type": "coil",
                "address": 2,
                "type": "switch"
            },
            {
                "name": "K4",
                "reg_type": "coil",
                "address": 3,
                "type": "switch"
            },
            {
                "name": "K5",
                "reg_type": "coil",
                "address": 4,
                "type": "switch"
            },
            {
                "name": "K6",
                "reg_type": "coil",
                "address": 5,
                "type": "switch"
            },
            {
                "name": "K7",
                "reg_type": "coil",
                "address": 6,
                "type": "switch"
            },
            {
                "name": "K8",
                "reg_type": "coil",
                "address": 7,
                "type": "switch"
            }
        ]
    }
}

В этом шаблоне следует обратить внимание на следующие параметры устройства.

    "protocol": "modbus_io",
    "stride": 1000,
    "shift": 500,
    "max_read_registers": 0,
    "setup": [

Параметр "protocol": "modbus_io" указывает, что устройство подключается по специальному протоколу к модулю расширения. Параметры "stride": 1000 и "shift": 500 задают сдвиг регистров в зависимости от того, в каком порядке подключены устройства к модулю расширения.

Сдвиг регистров (число, которое нужно добавить к базовому номеру регистра) вычисляется по формуле:

Shift = (((SlaveId.Secondary - 1) % 4) + 1) * DeviceConfig()->Stride + DeviceConfig()->Shift;

Здесь stride — DeviceConfig()->Stride, a shift — DeviceConfig()->Shift. Каждое подключенное устройство имеет порядковый номер SlaveId.Secondary, который начинается с 1 для первого устройства. В формуле знаком % обозначается деление по модулю. Например, подставляя значения shift и stride из шаблона в формулу, получаем:

Shift = ((( 1 - 1) % 4) + 1) * 500 + 1000 = (0 + 1) * 500 + 1000 = 1500

То есть ко всем регистрам, указанным в шаблоне, надо добавить 1500.

Еще один важный параметр — setup-секция:

     "setup": [    {
             "title": "IODIR",
             "address": 10000,
             "value": "0x0000"
         },
     {},
     ...]

WB-MAP3E

Устройство WB-MAP3E — трехканальный счетчик электроэнергии. Он измеряет большое количество параметров электрической сети. Поскольку опрос всех регистров такого устройства может занять достаточно много времени, есть несколько шаблонов, содержащих оптимальный набор параметров для решения текущей задачи. Мы рассмотрим шаблон из поставки драйвера.

{
  "device_type": "WB-MAP3E fw2",
  "device": {
    "name": "WB-MAP3E fw2",
    "id": "wb-map3e",
    "max_read_registers": 60,
    "response_timeout_ms": 1,
    "frame_timeout_ms": 15,
    "channels": [
      {
        "name": "L1",
        "id": "",
        "device_type": "phase L1"
      },
      {
        "name": "L2",
        "id": "",
        "device_type": "phase L2",
        "stride": 1
      },
      {
        "name": "L3",
        "id": "",
        "device_type": "phase L3",
        "stride": 2
      },
      {
        "name": "Total P",
        "reg_type": "input",
        "address": "0x1300",
        "type": "power",
        "format": "s32",
        "scale": 0.00512,
        "round_to": 0.01
      },
      {
        "name": "Total Q",
        "reg_type": "input",
        "address": "0x1308",
        "type": "value",
        "format": "s32",
        "scale": 0.00512,
        "round_to": 0.01
      },
      {
        "name": "Total S",
        "reg_type": "input",
        "address": "0x1310",
        "type": "value",
        "format": "s32",
        "scale": 0.00512,
        "round_to": 0.01
      },
      {
        "name": "Total PF",
        "reg_type": "input",
        "address": "0x10bc",
        "type": "value",
        "format": "s16",
        "scale": 0.001,
        "round_to": 0.01
      },
      {
        "name": "Total AP energy",
        "reg_type": "input",
        "address": "0x1200",
        "type": "power_consumption",
        "format": "u64",
        "scale": 1e-05,
        "round_to": 1e-06,
        "word_order": "little_endian"
      },
      {
        "name": "Total RP energy",
        "reg_type": "input",
        "address": "0x1220",
        "type": "value",
        "format": "u64",
        "scale": 1e-05,
        "round_to": 1e-06,
        "word_order": "little_endian"
      },
      {
        "name": "Frequency",
        "reg_type": "input",
        "address": "0x10f8",
        "type": "value",
        "format": "u16",
        "scale": 0.01,
        "round_to": 0.01
      },
      {
        "name": "Uptime",
        "reg_type": "input",
        "address": 104,
        "format": "u32",
        "enabled": false
      },
      {
        "name": "Supply voltage",
        "reg_type": "input",
        "address": 121,
        "scale": 0.001,
        "type": "voltage",
        "enabled": false
      }
    ],
    "subdevices": [
      {
        "device_type": "phase L1",
        "device": {
          "parameters": {
            "turns": {
              "title": "Turns",
              "order": 1,
              "address": "0x1460",
              "min": 0
            },
            "phi": {
              "title": "Phi",
              "order": 2,
              "address": "0x1463",
              "min": 0
            }
          },
          "channels": [
            {
              "name": "Urms",
              "id": "Urms L1",
              "reg_type": "input",
              "address": "0x1410",
              "type": "voltage",
              "format": "u32",
              "scale": 1.52588e-07,
              "round_to": 0.001
            },
            {
              "name": "Upeak",
              "id": "Upeak L1",
              "reg_type": "input",
              "address": "0x1810",
              "type": "voltage",
              "format": "s32",
              "scale": 0.01,
              "round_to": 0.01,
              "word_order": "little_endian"
            },
            {
              "name": "Voltage angle",
              "id": "Voltage angle L1",
              "reg_type": "input",
              "address": "0x10fd",
              "type": "value",
              "format": "s16",
              "scale": 0.1,
              "round_to": 0.01
            },
            {
              "name": "Irms",
              "id": "Irms L1",
              "reg_type": "input",
              "address": "0x1416",
              "type": "value",
              "format": "u32",
              "scale": 2.44141e-07,
              "round_to": 0.0001
            },
            {
              "name": "Ipeak",
              "id": "Ipeak L1",
              "reg_type": "input",
              "address": "0x1818",
              "type": "value",
              "format": "s32",
              "scale": 0.016,
              "round_to": 0.01,
              "word_order": "little_endian"
            },
            {
              "name": "P",
              "id": "P L1",
              "reg_type": "input",
              "address": "0x1302",
              "type": "power",
              "format": "s32",
              "scale": 0.00512,
              "round_to": 0.01
            },
            {
              "name": "Q",
              "id": "Q L1",
              "reg_type": "input",
              "address": "0x130a",
              "type": "value",
              "format": "s32",
              "scale": 0.00512,
              "round_to": 0.01
            },
            {
              "name": "S",
              "id": "S L1",
              "reg_type": "input",
              "address": "0x1312",
              "type": "value",
              "format": "s32",
              "scale": 0.00512,
              "round_to": 0.01
            },
            {
              "name": "PF",
              "id": "PF L1",
              "reg_type": "input",
              "address": "0x10bd",
              "type": "value",
              "format": "s16",
              "scale": 0.001,
              "round_to": 0.01
            },
            {
              "name": "AP energy",
              "id": "AP energy L1",
              "reg_type": "input",
              "address": "0x1204",
              "type": "power_consumption",
              "format": "u64",
              "scale": 1e-05,
              "round_to": 1e-06,
              "word_order": "little_endian"
            },
            {
              "name": "RP energy",
              "id": "RP energy L1",
              "reg_type": "input",
              "address": "0x1224",
              "type": "value",
              "format": "u64",
              "scale": 1e-05,
              "round_to": 1e-06,
              "word_order": "little_endian"
            },
            {
              "name": "Phase angle",
              "id": "Phase angle L1",
              "reg_type": "input",
              "address": "0x10f9",
              "type": "value",
              "format": "s16",
              "scale": 0.1,
              "round_to": 0.01
            }
          ]
        }
      },
      {
        "device_type": "phase L2",
        "device": {
          "parameters": {
            "turns": {
              "title": "Turns",
              "order": 1,
              "address": "0x1460",
              "min": 0
            },
            "phi": {
              "title": "Phi",
              "order": 2,
              "address": "0x1463",
              "min": 0
            }
          },
          "channels": [
            {
              "name": "Urms",
              "id": "Urms L2",
              "reg_type": "input",
              "address": "0x1410",
              "type": "voltage",
              "format": "u32",
              "scale": 1.52588e-07,
              "round_to": 0.001
            },
            {
              "name": "Upeak",
              "id": "Upeak L2",
              "reg_type": "input",
              "address": "0x1810",
              "type": "voltage",
              "format": "s32",
              "scale": 0.01,
              "round_to": 0.01,
              "word_order": "little_endian"
            },
            {
              "name": "Voltage angle",
              "id": "Voltage angle L2",
              "reg_type": "input",
              "address": "0x10fd",
              "type": "value",
              "format": "s16",
              "scale": 0.1,
              "round_to": 0.01
            },
            {
              "name": "Irms",
              "id": "Irms L2",
              "reg_type": "input",
              "address": "0x1416",
              "type": "value",
              "format": "u32",
              "scale": 2.44141e-07,
              "round_to": 0.0001
            },
            {
              "name": "Ipeak",
              "id": "Ipeak L2",
              "reg_type": "input",
              "address": "0x1818",
              "type": "value",
              "format": "s32",
              "scale": 0.016,
              "round_to": 0.01,
              "word_order": "little_endian"
            },
            {
              "name": "P",
              "id": "P L2",
              "reg_type": "input",
              "address": "0x1302",
              "type": "power",
              "format": "s32",
              "scale": 0.00512,
              "round_to": 0.01
            },
            {
              "name": "Q",
              "id": "Q L2",
              "reg_type": "input",
              "address": "0x130a",
              "type": "value",
              "format": "s32",
              "scale": 0.00512,
              "round_to": 0.01
            },
            {
              "name": "S",
              "id": "S L2",
              "reg_type": "input",
              "address": "0x1312",
              "type": "value",
              "format": "s32",
              "scale": 0.00512,
              "round_to": 0.01
            },
            {
              "name": "PF",
              "id": "PF L2",
              "reg_type": "input",
              "address": "0x10bd",
              "type": "value",
              "format": "s16",
              "scale": 0.001,
              "round_to": 0.01
            },
            {
              "name": "AP energy",
              "id": "AP energy L2",
              "reg_type": "input",
              "address": "0x1204",
              "type": "power_consumption",
              "format": "u64",
              "scale": 1e-05,
              "round_to": 1e-06,
              "word_order": "little_endian"
            },
            {
              "name": "RP energy",
              "id": "RP energy L2",
              "reg_type": "input",
              "address": "0x1224",
              "type": "value",
              "format": "u64",
              "scale": 1e-05,
              "round_to": 1e-06,
              "word_order": "little_endian"
            },
            {
              "name": "Phase angle",
              "id": "Phase angle L2",
              "reg_type": "input",
              "address": "0x10f9",
              "type": "value",
              "format": "s16",
              "scale": 0.1,
              "round_to": 0.01
            }
          ]
        }
      },
      {
        "device_type": "phase L3",
        "device": {
          "parameters": {
            "turns": {
              "title": "Turns",
              "order": 1,
              "address": "0x1460",
              "min": 0
            },
            "phi": {
              "title": "Phi",
              "order": 2,
              "address": "0x1463",
              "min": 0
            }
          },
          "channels": [
            {
              "name": "Urms",
              "id": "Urms L3",
              "reg_type": "input",
              "address": "0x1410",
              "type": "voltage",
              "format": "u32",
              "scale": 1.52588e-07,
              "round_to": 0.001
            },
            {
              "name": "Upeak",
              "id": "Upeak L3",
              "reg_type": "input",
              "address": "0x1810",
              "type": "voltage",
              "format": "s32",
              "scale": 0.01,
              "round_to": 0.01,
              "word_order": "little_endian"
            },
            {
              "name": "Voltage angle",
              "id": "Voltage angle L3",
              "reg_type": "input",
              "address": "0x10fd",
              "type": "value",
              "format": "s16",
              "scale": 0.1,
              "round_to": 0.01
            },
            {
              "name": "Irms",
              "id": "Irms L3",
              "reg_type": "input",
              "address": "0x1416",
              "type": "value",
              "format": "u32",
              "scale": 2.44141e-07,
              "round_to": 0.0001
            },
            {
              "name": "Ipeak",
              "id": "Ipeak L3",
              "reg_type": "input",
              "address": "0x1818",
              "type": "value",
              "format": "s32",
              "scale": 0.016,
              "round_to": 0.01,
              "word_order": "little_endian"
            },
            {
              "name": "P",
              "id": "P L3",
              "reg_type": "input",
              "address": "0x1302",
              "type": "power",
              "format": "s32",
              "scale": 0.00512,
              "round_to": 0.01
            },
            {
              "name": "Q",
              "id": "Q L3",
              "reg_type": "input",
              "address": "0x130a",
              "type": "value",
              "format": "s32",
              "scale": 0.00512,
              "round_to": 0.01
            },
            {
              "name": "S",
              "id": "S L3",
              "reg_type": "input",
              "address": "0x1312",
              "type": "value",
              "format": "s32",
              "scale": 0.00512,
              "round_to": 0.01
            },
            {
              "name": "PF",
              "id": "PF L3",
              "reg_type": "input",
              "address": "0x10bd",
              "type": "value",
              "format": "s16",
              "scale": 0.001,
              "round_to": 0.01
            },
            {
              "name": "AP energy",
              "id": "AP energy L3",
              "reg_type": "input",
              "address": "0x1204",
              "type": "power_consumption",
              "format": "u64",
              "scale": 1e-05,
              "round_to": 1e-06,
              "word_order": "little_endian"
            },
            {
              "name": "RP energy",
              "id": "RP energy L3",
              "reg_type": "input",
              "address": "0x1224",
              "type": "value",
              "format": "u64",
              "scale": 1e-05,
              "round_to": 1e-06,
              "word_order": "little_endian"
            },
            {
              "name": "Phase angle",
              "id": "Phase angle L3",
              "reg_type": "input",
              "address": "0x10f9",
              "type": "value",
              "format": "s16",
              "scale": 0.1,
              "round_to": 0.01
            }
          ]
        }
      }
    ]
  }
}

Обратите внимание — в параметрах устройства мы указываем, что одновременно следует считывать не более 60 регистров, чтобы не останавливать надолго опрос остальных устройств: "max_read_registers": 60.

Также показательным является параметр, описывающий накопленную реактивную энергию по фазе L1:

{
  "name": "RP energy",
  "id": "RP energy L1",
  "reg_type": "input",
  "address": "0x1224",
  "type": "value",
  "format": "u64",
  "scale": 1e-05,
  "round_to": 1e-06,
  "word_order": "little_endian"
},

Здесь стоит обратить внимание на то, что адрес регистра может задаваться и в шестнадцатеричном виде: "address": "0x1224", вещественный коэффициент масштабирования можно задавать в экспоненциальной записи: "scale": 1e-05, при считывании значения округлять его до нужного порядка: "round_to": 1e-06, а также учитывать, что число в modbus-регистрах хранится в порядке от младшего к старшему: "word_order": "little_endian".

Форматы хранения для 16-битных modbus-регистров описываются следующим образом:

        big-endian    : ( [0xAA 0xBB] [0xCC 0xDD] => 0xAABBCCDD ) 
        little-endian : ( [0xAA 0xBB] [0xCC 0xDD] => 0xCCDDAABB )

По умолчанию предполагается формат big-endian.

Полезные ссылки