Телеметрийный протокол Graupner HoTT

Продолжим вещание про Hott и его телеметрию. Сегодня опишу протокол взаимодействия разнообразных модулей Hott с приемниками.
Стоит сразу сказать, что система позволяет взаимодействовать с двумя основными типами устройств: со стандартными модулями Hott и с нестандартными. В первом случае стандартный модуль телеметрии передает структуру данных, в которой пульт уже знает, что первый байт это напряжение, второй температура, третий обороты и т.п. Информация, содержащаяся в такой структуре, просто форматируется и распихивается по стандартным окошкам телеметрии в пульте. Этот режим называется binary. Во втором случае от модуля, являющегося не стандартным Hott, поступает информация в виде текста, описывающего отображаемый параметр, и самого значения параметра. То есть у нас есть возможность создавать кастомный экран на пульте и отображать нужные нам данные. Ко всему тут появляется возможность передачи данных в обратном направлении (настройка параметров модуля через пульт). Этот режим называется text (текстовый). В нем я до конца не разобрался, потому как для моих целей вполне достаточен подлог своих данных в структуры данных стандартных модулей Hott. В виду этого речь дальше пойдет о binary mode (числовой режим, наверное по-русски это будет примерно так).
На самом деле, все тут у нас очень и очень просто. На физическом уровне обмен осуществляется средствами протокола UART, на скорости 19200 бод, 8 бит данных, 1 стоп бит, без четности. Используются два провода: земля и сигнал. Поэтому в модуле у микроконтроллера необходимо ноги Rx и Tx замкнуть и подключить к сигнальной линии, аппаратный UART постоянно переключать с режима передачи на режим приема. То есть, после инициализации UART настроен на прием. Как только услышали нужную команду, переключаемся на передачу, отправляем структуру данных, переключаемся на прием и ждем следующую команду.
Приемник каждые 200 мсек посылает на линию два байта вида:

0x80 0x89…
0x80 0x8e…
0x80 0x8d…,

где первый байт (0x80) показывает, что общение идет в binary mode, а второй байт, это идентификатор модуля, от которого приемник сейчас ждет ответ. Услышали вторым байтом идентификатор своего модуля, зарядили в ответ структуру данных этого модуля. Между приемом идентификатора своего модуля и отправкой первого байта структуры данных этого модуля по спецификации проткола положена задержка в 5 мсек. Плюс ко всему, между отправками байтов структуры должны быть задержки 2 мсек (я использовал 3 мсек). То есть схематично это выглядит так:
(Приемник ->0x80 0x8e) задержка 5 мсек (модуль->первый байт) задержка 2 мсек (модуль->второй байт) задержка 2 мсек (модуль->третий байт) задержка 2 мсек … (модуль->последний байт).
Вот и все.
Тут есть один момент: через некоторое время (секунд 5-10) после включения пульта приемник в линию телеметрии перестает выдавать запросы на конкретные модули, если не получает ответы от них, а просто выдает два байта 0x80 0x80. По крайней мере на моей прошивке пульта и приемника так. А мой HeliModule отвечал строго на запрос идентификатора Electrik Air модуля (0x8e). Соответственно, если не успеть включить модуль до конца перебора приемником идентификаторов известных ему модулей, то модуль просто не определяется. Причем как я понял, обмен весь инициирует и поддерживает пульт через эфир и приемник, потому как если при включенном приемнике перезагрузить пульт, перебор идентификаторов модулей опять восстанавливается. Это было жутко не удобно, потому что приходилось либо очень быстро включать приемник и модуль, либо при включенном борте на модели перезагружать пульт, что противоречит общеизвестному порядку включения аппаратуры РУ. Я попробовал отвечать своей структурой данных на любой запрос приемника, даже на два байт 0x80 0x80, и фокус удался (благо в структуре данных, посылаемых модулем, так же содержится тот самый идентификатор, по которому в конечном счете пульт и понимает, что же это за данные). В итоге ограничение по времени на подключение борта удалось обойти.
Теперь о структуре данных. Я использовал структуру данных стандартного электрик аир модуля. Его в качестве примера и приведу и прокомментирую каждый байт этой структуры (если я понял, что это за байт))). Думаю тут все будет предельно понятно. Работа со всеми остальными структурами аналогична.
Структура данных на языке Си объявляется так:

struct HOTT_EAM_MSG {
        int8_t start_byte;                          //#01 Старт байт. Обозначает начало структуры данных. Значение фиксировано - 0x7c;
        int8_t eam_sensor_id;                   //#02 Идентификатор електрик аир модуля - 0x8e
        int8_t warning_beeps;                   //#03 Как следует из названия, это предупреждающие писки пульта. Я попробовал пару,
                                                           они отличались друг от друга незначительно тоном. Все не пробовал, в своем проекте я
                                                           использовал только предупреждение понижения порога напряжения на банке. Логика тут думаю понятная.
                                                           1=A 2=B ... or 'A' - 0x40 = 1
                                                                // Q    Достигнута минимальное напряжение банки, датчик 1
                                                                // R    Достигнута минимальное напряжение батареи 1, датчик 1
                                                                // J    Достигнута максимальное напряжение батареи 1, датчик 1
                                                                // F    Достигнута минимальная температура, датчик 1
                                                                // H    Достигнута максимальная температура, датчик 1
                                                                // S    Достигнута минимальное напряжение батареи 2, датчик 2
                                                                // K    Достигнута максимальное напряжение батареи 2, датчик 2
                                                                // G    Достигнута минимальная температура, датчик 2
                                                                // I    Достигнута максимальная температура, датчик 2
                                                                // W   Достигнут максимальный ток
                                                                // V    Достигнут порог максимального потребления mAh
                                                                // P    Достигнуто минимально напряжение главного источника питания (не понял я, какой у них главный)
                                                                // X    Достигнуто максимальное напряжение главного источника питания (не понял я, какой у них главный)
                                                                // O    Достигнута минимальная высота
                                                                // Z    достигнута максимальная высота
                                                                // C    Скорость снижения m/sec слишком высокая
                                                                // B    Скорость снижения m3/sec слишком высокая
                                                                // N    Скорость подъема m/sec слишком высокая
                                                                // M    Скорость подъема m/3sec слишком высокая

        int8_t sensor_id;                   //#04 Какое-то стандартное значение, равное 0xe0
        int8_t alarm_invers1;                   //#05 Маска выделения значения на дисплее пульта (инверсное отображнение - темный фон, светлые символы)
                                                                //Bit#  Поле выделения (бит в байте)
                                                                // 0    mAh
                                                                // 1    Батарея 1 (наверное напряжение)
                                                                // 2    Батарея 2 (наверное напряжение)
                                                                // 3    Температура 1
                                                                // 4    Температура 2
                                                                // 5    Высота
                                                                // 6    Ток
                                                                // 7    Напряжение основного питания...
        int8_t alarm_invers2;                   //#06 Маска выделения значения на дисплее пульта (инверсное отображнение - темный фон, светлые символы)
                                                                //Bit#  Поле выделения (бит в байте)
                                                                // 0    m/s
                                                                // 1    m/3s
                                                                // 2    Высота (повтор?)
                                                                // 3    m/s     (повтор?)
                                                                // 4    m/3s (повтор?)
                                                                // 5    не используется/ неизвестен
                                                                // 6    не используется/ неизвестен
                                                                // 7    "ON" sign/text msg active (похоже, включает режим всплывающего окна, по типу Throttle too high...)

        int8_t cell1_L;                         //#07 Банка 1, минимальное значение. Шаг 0.02V, 124=2.48V
        int8_t cell2_L;                         //#08 Банка 2...
        int8_t cell3_L;                         //#09 Я просто вывел в эти поля значения напряжения банок
        int8_t cell4_L;                         //#10
        int8_t cell5_L;                         //#11
        int8_t cell6_L;                         //#12
        int8_t cell7_L;                         //#13
        int8_t cell1_H;                         //#14 Банка 1, максимальное значение. Шаг 0.02V, 124=2.48V
        int8_t cell2_H;                         //#15 Банка 2...
        int8_t cell3_H;                         //#16
        int8_t cell4_H;                         //#17
        int8_t cell5_H;                         //#18
        int8_t cell6_H;                         //#19
        int8_t cell7_H;                         //#20

        int8_t batt1_voltage_L;         //#21 Минимальное напряжение батареи 1, шаг 0,1V. Младший байт. Я просто вывел сюда напряжение своей батареи.
        int8_t batt1_voltage_H;         //#22 Старший байт.

        int8_t batt2_voltage_L;         //#23 Напряжение батареи 2, шаг 0,1V. Младший байт.
        int8_t batt2_voltage_H;         //#24 Старший байт.

        int8_t temp1;                                   //#25 Температура, датчик 1. Смещение на 20 единиц: 20 = 0°, 46 = 26°
        int8_t temp2;                                   //#26 Температура, датчик 1

        int8_t altitude_L;                      //#27 Высота. единицы: метр. Смещение на 500 единиц: 500 = 0m. Младший байт.
        int8_t altitude_H;                      //#28 Старший байт.

        int8_t current_L;                               //#29 Ток, шаг 0.1А. Младший байт
        int8_t current_H;                               //#30 Старший байт

        int8_t main_voltage_L;          //#30 Напряжение главного источника питания, шаг 0.1V, младший байт
        int8_t main_voltage_H;          //#31 Старший байт

        int8_t batt_cap_L;                      //#32 Использованная емкость батареи, шаг 10mAh, младший байт
        int8_t batt_cap_H;                     //#33 Использованная емкость батареи, шаг 10mAh, старший байт

        int8_t climbrate_L;                     //#34 Скорость подъема в 0.01m/s. Смещение - 30 000 единиц. 30000 = 0.00 m/s. Младший байт.
        int8_t climbrate_H;                     //#35 Старший байт. ХЗ, как они в двубайтном слове умудрились сделать смещение в 30 000 единиц. Не разбирался.

        int8_t climbrate3s;                     //#36 Скорость подъема в m/3sec. Смещение в 120 единиц. 120 = 0m/3sec

        int8_t rpm_L;                                   //#37 RPM. Шаг: 10 об/мин. Младший байт.
        int8_t rpm_H;                                   //#38 Старший байт.

        int8_t electric_min;                    //#39 Время работы потребителей тока, в минутах. Время начинает идти, когда ток потребления превышает  3A
        int8_t electric_sec;                    //#40 То же, секунды

        int8_t speed_L;                         //#41 Воздушная скорость, km/h. Шаг 1km/h. Младший байт.
        int8_t speed_H;                         //#42 Старший байт
        int8_t stop_byte;                      //#43 Стоп байт. Значение фиксировано - 0x7d;
//      int8_t parity;                          //#44 CRC, байт контрольной суммы. Байт равен сумме всех предыдущих байтов.
};

Ну вот собственно и все.

  • 1652
Comments
vadimip

Стоп-байты равны 0x00?

Статья классная. Мне попадалось несколько материалов по телеметрии hott, но кажется только сейчас окончательно дошло, что к чему.

sirQWERTY

Ну классно, что стало все понятно. Я чет наоборот опасался, что будет не очень наглядно.
Стоп байт 0x7d, забыл указать. Сейчас исправлю.