Общее описание

Документ описывает упрощенный протокол взаимодействия с устройством интернета вещей, предназначенный главным образом для организации взаимодействия с устройством со слабыми ресурсами с использованием любых каналов связи, способных передавать потоки данных (COM, TCP/IP и т. д.). Управляемое устройство может быть хабом, при этом оно объединяет несколько других устройств. В рамках данного документа предполагается, что происходит взаимодействие Устройства с неким Клиентом (в частности, им может быть сервер IoT, к которому подключено устройство).

Описание протокола

  1. Каждое Устройство имеет уникальный 128-битный идентификатор (Uuid).

  2. Каждое Устройство может иметь идентификатор типа (Uuid), определяемый производителем.

  3. Взаимодействие происходит путем двухстороннего обмена сообщениями. Каждое сообщение — строка байт, причем в протоколе в качестве разделителей и зарезервированных элементов используются строки ascii-символов. Сообщения разделяются байтом с десятичным кодом 10 (символ перевода строки, "\n" в языке С).

  4. Сообщения состоят из нескольких элементов, первый из которых называется заголовком, остальные аргументами. Элементы сообщения разделяются байтом с десятичным кодом 124 (символ "|").
    Пример сообщения:
    info|Argument 1|Argument 2|Argument 3\n

  5. Для экранирования служебных символов в сообщении используется байт с десятичным кодом 92 (символ обратной косой черты "\"). Экранирование символов производится по следующим правилам:

    1. Символ "\" экранируется им же. В сообщении — "\\".

    2. Символ "|" экранируется как "\|".

    3. Символ перевода строки заменяется экранированной буквой "n" (десятичный код 110). В сообщении — "\n".

    4. Символ с кодом 0 заменяется на экранированный "0" (десятичный код 48). В сообщении — "\0".

    5. Произвольный байт данных может быть записан как \xHH, где HH — шестнадцатеричный код символа в нижнем или верхнем регистре. Пример — "\x2f". Если шестнадцатеричный код не распознан, символ игнорируется.

    6. Символ с кодом 0 служит для сигнализации о том, что Устройство перезагрузилось и его состояние было сброшено.

Базовые сообщения

  1. Информационное сообщение от Устройства. Заголовок — info. Предназначено для передачи информации, не требующей специальной обработки (например, отладочная информация) и предназначенной для передачи ее человеку (разработчику или оператору). Сообщение не требует ответа. Аргументы сообщения — текстовые строки, содержащие информацию, предназначенную для прочтения человеком.

  2. Запрос идентификации Устройства от Клиента. Заголовок — identify. Аргументы отсутствуют. Устройство должно в течении 5 секунд вернуть ответное сообщение с заголовком deviceinfo со следующими аргументами:
    1. Уникальный идентификатор Устройства в виде текстового шестнадцатеричного представления UUID "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" или "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".
    2. Человеко-читаемое название (не должно быть в формате UUID).
    3. Идентификатор типа Устройства (UUID), может отсутствовать.

  3. Запрос идентификации Устройства-хаба от Клиента. Заголовок — identify. Аргументы отсутствуют. Устройство должно в течении 5 секунд вернуть ответное сообщение с заголовком deviceinfo со следующими аргументами:
    1. Специальный идентификатор "#hub".
    2. Уникальный идентификатор Устройства в виде текстового шестнадцатеричного представления UUID "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" или "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".
    3. Человеко-читаемое название.
    4. Идентификатор типа Устройства (UUID), может отсутствовать.

  4. Запрос на перечисление подключенных к Устройству-хабу дочерних устройств от Клиента. Заголовок — identify_hub. В ответ Устройство-хаб сразу возвращает ok либо err (если Устройство не является хабом). Позже дочерние устройства, подключенные к хабу, передают сообщения deviceinfo.

  5. Сообщение синхронизации от Клиента. Заголовок — sync. Предназначено для проверки состояния канала связи с Устройством. В ответ на это сообщение Устройство должно в течении 5 секунд вернуть сообщение syncr.

  6. Команда от Клиента. Заголовок — call. Первый аргумент — идентификатор вызова, второй — название команды (любое кроме зарезервированных, описанных в разделе "Зарезервированные названия команд"). Остальные аргументы сообщения — аргументы команды. Если Устройство не может выполнить команду в течение 5 секунд, оно должно регулярно передавать сообщение syncс с аргументом-идентификатором вызова. Устройство по окончании выполнения команды должно вернуть сообщение с заголовком ok в случае успешного завершения или err в случае неудачи, первый аргумент этих сообщений — идентификатор вызова, далее — возвращаемые значения (для сообщения ok) или описание ошибки (для сообщения err).

  7. Сообщение синхронизации команды. Заголовок — syncс. Если управляемое устройство не может выполнить команду быстрее 5 секунд, оно должно регулярно посылать сообщение syncc. Если в течение 5 секунд сообщение не пришло, команда считается невыполненной.

  8. Измерение в текстовом виде от Устройства. Заголовок — meas. Аргументы сообщения: название датчика и значение в текстовом виде, каждое число в отдельном аргументе сообщения. (см. Форматы данных сенсоров)

  9. Измерения в бинарном виде от Устройства. Заголовок — measb. Аргументы сообщения: название датчика, далее значение в бинарном виде, все передаваемые числа (включая временную метку) упакованы в один аргумент сообщения без разделителей. Порядок байт — от младшего к старшему. (см. Форматы данных сенсоров).

  10. Измерения в бинарном виде от Устройства, закодированные в base64. Заголовокmeasb64. Аргументы сообщения: название датчика, далее значение в текстовом виде, все передаваемые числа (включая временную метку) упакованы в один аргумент сообщения без разделителей, закодированный в base64. Порядок байт числаот младшего к старшему. (см. Форматы данных сенсоров).

  11. Сообщение об изменении состояния от Устройства. Сообщение — statechanged. Передается устройством, когда происходит изменение его состояния. Аргументы:
    1. Название команды или "#" (см. раздел "Состояние устройства").
    2. Номер аргумента команды (начиная с 1) или название дополнительного параметра устройства.
    3. Новое значение.

  12. Широковещательный запрос на поиск устройства (для сетей ptp, поддерживающих широковещательные запросы, например, UDP запрос в локальной IP сети). Заголовок — find_device. Аргумент — имя или идентификатор устройства, идентификатор сервера, возможны дополнительные аргументы, если это необходимо для идентификации сервера. Устройство с указанным именем или адресом, принявшее сообщение, может идентифицировать сервер (по аргументам или, например, адресу отправителя в IP сети) и подключиться к нему.

 

Отправка сообщений устройству, находящемуся за Устройством-хабом, происходит аналогично отправке сообщений самому устройству, но к каждому сообщению добавляется в начало два аргумента: специальный идентификатор "#hub" и идентификатор целевого устройства в виде текстового шестнадцатеричного представления UUID "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx". Аналогичным образом приходят ответные сообщения: заголовок "#hub", идентификатор отправителя, дальше само сообщение. Для отправки сообщения всем устройствам за хабом используется идентификатор "#broadcast".

 

Уведомления от хаба:

  1. Появление устройства за хабом. Заголовок — device_identified (после заголовка #hub и идентификатора отправителя. Аргумент — название устройства.
    Пример:
    #hub|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|device_identified|test1\n

  2. Отключение устройства от хаба. Заголовок — device_lost.
    Пример:
    #hub|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|device_lost\n

 

Зарезервированные названия команд

Название

Параметры

Описание и возвращаемые значения

#sensors

отсутствуют

Запрос списка датчиков. Возвращаемое значение — список датчиков (формат описан ниже в разделе "Формат описания датчиков")

#controls

отсутствуют

Запрос описания интерфейса управления. Возвращаемое значение — описание интерфейса управления (формат описан ниже в разделе "Формат описания интерфейса")

#state

отсутствуют

Запрос состояния устройства (см. раздел "Состояние устройства")

#setup

1 — идентификатор устройства

2 — имя устройства

Запись идентификатора и имени устройства в энергонезависимую память (команда может игнорироваться, если идентификатор и имя задаются при производстве устройства)

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

Формат описания датчиков (xml или json)

Описание датчиков представляет собой xml или json документ, которому соответствует список датчиков с атрибутами. Для каждого датчика указывается имя, отображаемое название, тип, единицы измерения (строка, например "оС", "м/с2" и т.д.) и дополнительные атрибуты. Имя сенсора уникально в пределах устройства. Тип датчика описывает формат передаваемых устройством данных и способ представления в сообщении. Форматы передаваемых значений описаны ниже в разделе Форматы данных датчиков. Описание датчиков передается между устройствами в xml или json формате, соответствующие схемы приложены в отдельных файлах sensors.xsd, sensors_simplified.xsd, sensors.json-schema, sensors_simplified.json-schema.

Примеры:

<sensors>
<sensor name="temperature" title="Temperature in celsius from 1st and 2nd thermometers" type="s_f32_d2" unit="оС">
<attributes min="-50" max="50"/>
</sensor>
<sensor><!--sensor definition--></sensor>
</sensors>

{"sensors" : [
{
"name" : "temperature",
"title" : " Temperature in celsius",
"type" : "s_f32",
"unit" : "оС",
"attributes" :
{
"min" : "-50",
"max" : "50"
}
},
{
//sensor definition
}
]}

Форматы данных датчиков

В различных форматах могут использоваться метки локального и глобального времени. Локальное время — время, измеряемое на устройстве от какой-то фиксированной точки (но не известной), например — от момента включения устройства. Глобальное время — количество миллисекунд, прошедшее с 01.01.1970г. В бинарном виде временная метка — всегда 64-битное целое число со знаком. Использование глобального времени возможно при наличии на устройстве часов реального времени. Временная метка всегда передается в начале измерения.

Строка формата датчика представляет собой набор ключей, разделенных символом "_". Ключ представляет собой набор символов, которые объединены в группы. Из каждой группы может присутствовать только один ключ.

Ключи, описывающие тип чисел в измерениях

Ключ

Описание

f32

Вещественное число одинарной точности согласно стандарту IEEE 754. В языке C — тип float.

f64

Вещественное число двойной точности согласно стандарту IEEE 754. В языке C — тип double.

s8

Целое число со знаком размером 8 бит (1 байт). Значения от -128 до 128

u8

Целое число без знака размером 1 байт. Значения от 0 до 255

s16

Целое число со знаком размером 2 байта. Значения от -32768 до 32767

u16

Целое число без знака размером 2 байта. Значения от 0 до 65535

s32

Целое число со знаком размером 4 байта. Значения от -2147483648 до 2147483647

u32

Целое число без знака размером 4 байта. Значения от 0 до 4294967295

s64

Целое число со знаком размером 8 байт. Значения от -9223372036854775808 до 9223372 036854775807

u64

Целое число без знака размером 8 байт. Значения от 0 до 18446744073709551615

txt

Вместо чисел передается текст в кодировке UTF-8. Для этого типа не используются сообщения measb и measb64


 

Ключ, описывающий размерность отсчета:

Ключ

Описание

dXX

XX — размерность отсчета, целое число >=1. Например, d2, d3, d45. Если ключ не указан, размерность по-умолчанию — 1.


 

Ключи, описывающие количество значений в одном измерении:

Ключ

Описание

sv

Сокращение от "single value".

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

pv

Сокращение от "packet value".

Измерение представляет собой набор многомерных отсчетов с размерностью, указанной в описании датчика. Количество отсчетов в наборе 1 и более, может варьироваться в каждом измерении.

 

Ключи, описывающие временную метку измерения:

Ключ

Описание

lt

Временная метка локального времени устройства. Может быть в любых единицах измерения (секунды, милли-, микро- или наносекунды, тики и т. д.).

gt

Временная метка глобального времени — количество миллисекунд, прошедшее с 01.01.1970г.

nt

Временная метка отсутствует. Вариант по-умолчанию, если ни один из ключей не указан.

 

Примеры (название датчика — всегда test):

Формат

Описание

sv_f32_d3_gt

Измерение — единичный трехмерный отсчет, состоящий из трех значений с плавающей точкой одинарной точности и метки глобального времени

Пример:

meas|test|1532516864977|12.0|16.3|67.9

sv_u32

Измерение — единичный одномерный отсчет, содержащий целое 32-битное число без знака

Пример:

meas|test|100500

pv_d2_u8_lt

Измерение — пакет, содержащий метку локального времени и набор двумерных отсчетов из 1-байтовых беззнаковых значений

Пример:

meas|test|123456|3|27|56|1

meas|test|654321|67|12|252|22|56|12

123456 и 654321 — метки локального времени, в первом примере пакет содержит 2 отсчета по два значения, во втором — 3 отсчета.

 

Формат описания интерфейса

Элементы управления устройством объединены в группы, содержащие сами элементы управления и дочерние группы. В группе может быть задано горизонтальное или вертикальное размещение элементов. Если не задано — по умолчанию по вертикали.

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

Если параметры для элемента управления не заданы, отображается кнопка, при нажатии отправляется команда без параметров. Если задан один параметр, генерируется один элемент UI в зависимости от типа параметра, при взаимодействии с которым отправляется команда, и кнопка только при необходимости, например, для поля ввода. Если параметры заданы, но присутствует атрибут "force_button" со значением "1", генерируется набор элементов UI согласно заданному размещению, соответствующих типам параметров, и кнопка, при нажатии на которую отправляется команда. Если атрибут "force_button" отсутствует, команда генерируется автоматически при изменении состояния элементов UI. Каждый параметр команды имеет набор атрибутов, различающихся в зависимости от типа. Если присутствует атрибут "button_text", он задает текст на кнопке.

Описание элементов управления передается между устройствами в xml или json формате, соответствующие схемы приложены в отдельных файлах controls.xsd, controls_simplified.xsd, controls.json-schema, controls_simplified.json-schema.

Примеры:

<controls>
<group title="$title" layout="v|h">
<group title="$title" layout="v|h">
<control title="$title" command="$cmd" layout="v|h">
<param title="$title" type="$type">
<attributes key1="$value1" key2="$value2"/>
</param>
<param><!--param definition--></param>
</control>
<control title="$title" command="$cmd" layout="v|h" force_button="1" button_text="Some text">
<!-- control definition-->
</control>
</group>
<control><!--command definition--></control>
<group><!--group definition--></group>
</group>
</controls>

{"controls" : {
"element_type" : "group",
"title" : "$title",
"layout" : "v|h",
"elements" :
[
{
"element_type" : "group"
//group definition
},
{
"element_type" : "control",
"title" : "$title",
"layout" : "v|h",
"command" : "$command",
"button_text": "Some text",
"params" : [
{
"title" : "$title",
"type" : "$type",
"constraints" :
{
"$key1" : "$value1",
"$key2" : "$value2"
}
}
]
}

]
}}

Типы параметра и возможные ограничения

Тип параметра

Возможные ограничения

Элемент UI

checkbox

onValue — значение, передаваемое, когда чекбокс включен (по умолчанию "1")

offValue — значение, передаваемое, когда чекбокс выключен (по умолчанию "0")

Чекбокс, checkable кпопка

 

text_edit

placeholder — текст, отображаемый, если поле пустое

Поле ввода (с кнопкой отправки справа, если параметр у контрола один)

 

select

values — список значений, разделенных символом "|" (если отсутствует, всегда отправляется "0")

titles — список отображаемых названий для значений, разделенных символом "|"

Выпадающий список

 

slider

min — минимальное значение (по умолчанию 0)

max — минимальное значение (по умолчанию 1023)

step — величина шаг (по умолчанию 1)

layout — вид слайдера ("h" — горизонтальный, "v" - вертикальный)

Слайдер

 

dial

min — минимальное значение (по умолчанию 0)

max — минимальное значение (по умолчанию 1023)

step — величина шаг (по умолчанию 1)

"Крутилка"

 

radio

values - список значений, разделенных символом "|" (если отсутствует или пустой, элемент не отображается, передается значение "0")

titles — список отображаемых названий для значений, разделенных символом "|"

Группа радиокнопок, из которых может быть нажата только одна

 

hidden

value — отправляемое значение ("0", если отсутствует)

Не отображается в интерфейсе


 

Состояние устройства

Состояние управляющего интерфейса устройства описывает текущие значения параметров команд устройства и значения дополнительных параметров, не связанных с интерфейсом управления. При изменении состояния устройство оповещает об этом подключенного клиента при помощи специального сообщения "statechanged". Аргументы сообщения сгруппированы по 3. Первым аргументом в группе идет команда, параметр которой изменился, или "#", если изменился дополнительный параметр, не связанный ни с какой командой. В первом случае вторым аргументом в группе идет номер аргумента команды (начиная с 1), во втором — название дополнительного параметра. Третий аргумент в группе — значение параметра. При запросе всего состояния устройства при помощи зарезервированной команды "#state" возвращается все состояние устройства в одном сообщении с заголовком "ok" и аргументами, сгруппированными по 3 по аналогии с сообщением "statechanged".