Документ описывает упрощенный протокол взаимодействия с устройством интернета вещей, предназначенный главным образом для организации взаимодействия с устройством со слабыми ресурсами с использованием любых каналов связи, способных передавать потоки данных (COM, TCP/IP и т. д.). Управляемое устройство может быть хабом, при этом оно объединяет несколько других устройств. В рамках данного документа предполагается, что происходит взаимодействие Устройства с неким Клиентом (в частности, им может быть сервер IoT, к которому подключено устройство).
Каждое Устройство имеет уникальный 128-битный идентификатор (Uuid).
Каждое Устройство может иметь идентификатор типа (Uuid), определяемый производителем.
Взаимодействие происходит путем двухстороннего обмена сообщениями. Каждое сообщение — строка байт, причем в протоколе в качестве разделителей и зарезервированных элементов используются строки ascii-символов. Сообщения разделяются байтом с десятичным кодом 10 (символ перевода строки, "\n" в языке С).
Сообщения состоят из нескольких элементов, первый из которых называется заголовком, остальные аргументами. Элементы сообщения разделяются байтом с десятичным кодом 124 (символ "|").
Пример сообщения:
info|Argument 1|Argument 2|Argument 3\n
Для экранирования служебных символов в сообщении используется байт с десятичным кодом 92 (символ обратной косой черты "\"). Экранирование символов производится по следующим правилам:
Символ "\" экранируется им же. В сообщении — "\\".
Символ "|" экранируется как "\|".
Символ перевода строки заменяется экранированной буквой "n" (десятичный код 110). В сообщении — "\n".
Символ с кодом 0 заменяется на экранированный "0" (десятичный код 48). В сообщении — "\0".
Произвольный байт данных может быть записан как \xHH, где HH — шестнадцатеричный код символа в нижнем или верхнем регистре. Пример — "\x2f". Если шестнадцатеричный код не распознан, символ игнорируется.
Символ с кодом 0 служит для сигнализации о том, что Устройство перезагрузилось и его состояние было сброшено.
Информационное сообщение от Устройства. Заголовок — info. Предназначено для передачи информации, не требующей специальной обработки (например, отладочная информация) и предназначенной для передачи ее человеку (разработчику или оператору). Сообщение не требует ответа. Аргументы сообщения — текстовые строки, содержащие информацию, предназначенную для прочтения человеком.
Запрос идентификации Устройства от Клиента. Заголовок — identify. Аргументы отсутствуют. Устройство должно в течении 5 секунд вернуть ответное сообщение с заголовком deviceinfo со следующими аргументами:
1. Уникальный идентификатор Устройства в виде текстового шестнадцатеричного представления UUID "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" или "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".
2. Человеко-читаемое название (не должно быть в формате UUID).
3. Идентификатор типа Устройства (UUID), может отсутствовать.
Запрос идентификации Устройства-хаба от Клиента. Заголовок — identify. Аргументы отсутствуют. Устройство должно в течении 5 секунд вернуть ответное сообщение с заголовком deviceinfo со следующими аргументами:
1. Специальный идентификатор "#hub".
2. Уникальный идентификатор Устройства в виде текстового шестнадцатеричного представления UUID "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" или "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".
3. Человеко-читаемое название.
4. Идентификатор типа Устройства (UUID), может отсутствовать.
Запрос на перечисление подключенных к Устройству-хабу дочерних устройств от Клиента. Заголовок — identify_hub. В ответ Устройство-хаб сразу возвращает ok либо err (если Устройство не является хабом). Позже дочерние устройства, подключенные к хабу, передают сообщения deviceinfo.
Сообщение синхронизации от Клиента. Заголовок — sync. Предназначено для проверки состояния канала связи с Устройством. В ответ на это сообщение Устройство должно в течении 5 секунд вернуть сообщение syncr.
Команда от Клиента. Заголовок — call. Первый аргумент — идентификатор вызова, второй — название команды (любое кроме зарезервированных, описанных в разделе "Зарезервированные названия команд"). Остальные аргументы сообщения — аргументы команды. Если Устройство не может выполнить команду в течение 5 секунд, оно должно регулярно передавать сообщение syncс с аргументом-идентификатором вызова. Устройство по окончании выполнения команды должно вернуть сообщение с заголовком ok в случае успешного завершения или err в случае неудачи, первый аргумент этих сообщений — идентификатор вызова, далее — возвращаемые значения (для сообщения ok) или описание ошибки (для сообщения err).
Сообщение синхронизации команды. Заголовок — syncс. Если управляемое устройство не может выполнить команду быстрее 5 секунд, оно должно регулярно посылать сообщение syncc. Если в течение 5 секунд сообщение не пришло, команда считается невыполненной.
Измерение в текстовом виде от Устройства. Заголовок — meas. Аргументы сообщения: название датчика и значение в текстовом виде, каждое число в отдельном аргументе сообщения. (см. Форматы данных сенсоров)
Измерения в бинарном виде от Устройства. Заголовок — measb. Аргументы сообщения: название датчика, далее значение в бинарном виде, все передаваемые числа (включая временную метку) упакованы в один аргумент сообщения без разделителей. Порядок байт — от младшего к старшему. (см. Форматы данных сенсоров).
Измерения в бинарном виде от Устройства, закодированные в base64. Заголовок — measb64. Аргументы сообщения: название датчика, далее значение в текстовом виде, все передаваемые числа (включая временную метку) упакованы в один аргумент сообщения без разделителей, закодированный в base64. Порядок байт числа — от младшего к старшему. (см. Форматы данных сенсоров).
Сообщение об изменении состояния от Устройства. Сообщение — statechanged. Передается устройством, когда происходит изменение его состояния. Аргументы:
1. Название команды или "#" (см. раздел "Состояние устройства").
2. Номер аргумента команды (начиная с 1) или название дополнительного параметра устройства.
3. Новое значение.
Широковещательный запрос на поиск устройства (для сетей ptp, поддерживающих широковещательные запросы, например, UDP запрос в локальной IP сети). Заголовок — find_device. Аргумент — имя или идентификатор устройства, идентификатор сервера, возможны дополнительные аргументы, если это необходимо для идентификации сервера. Устройство с указанным именем или адресом, принявшее сообщение, может идентифицировать сервер (по аргументам или, например, адресу отправителя в IP сети) и подключиться к нему.
Отправка сообщений устройству, находящемуся за Устройством-хабом, происходит аналогично отправке сообщений самому устройству, но к каждому сообщению добавляется в начало два аргумента: специальный идентификатор "#hub" и идентификатор целевого устройства в виде текстового шестнадцатеричного представления UUID "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx". Аналогичным образом приходят ответные сообщения: заголовок "#hub", идентификатор отправителя, дальше само сообщение. Для отправки сообщения всем устройствам за хабом используется идентификатор "#broadcast".
Уведомления от хаба:
Появление устройства за хабом. Заголовок — device_identified (после заголовка #hub и идентификатора отправителя. Аргумент — название устройства.
Пример:
#hub|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|device_identified|test1\n
Отключение устройства от хаба. Заголовок — device_lost.
Пример:
#hub|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|device_lost\n
Название |
Параметры |
Описание и возвращаемые значения |
---|---|---|
#sensors |
отсутствуют |
Запрос списка датчиков. Возвращаемое значение — список датчиков (формат описан ниже в разделе "Формат описания датчиков") |
#controls |
отсутствуют |
Запрос описания интерфейса управления. Возвращаемое значение — описание интерфейса управления (формат описан ниже в разделе "Формат описания интерфейса") |
#state |
отсутствуют |
Запрос состояния устройства (см. раздел "Состояние устройства") |
#setup |
1 — идентификатор устройства 2 — имя устройства |
Запись идентификатора и имени устройства в энергонезависимую память (команда может игнорироваться, если идентификатор и имя задаются при производстве устройства) |
При расширении списка зарезервированных команд в дальнейшем их названию будут начинаться с "#".
Описание датчиков представляет собой 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 кпопка
|
Поле ввода (с кнопкой отправки справа, если параметр у контрола один)
|
||
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".