В этом тексте я напишу атрибуты хорошего и простого Master<->Slave протокола канального уровня для пакетного обмена информацией между устройствами на общих шинах таких как RS485, CAN, LoRa, BLE, Lin или соединений типа точка-точка UART, RS232.
Несмотря на то, что есть стандартизированные канальные протоколы ModBus, DLMS, RDS, UBX, NEC, Pelco-D, yModem, многие российские компании всё же колхозят свои собственные внутренние протоколы для взаимодействия между электронными платами.
Тут представлены общие атрибуты и пожелания для таких доморощенных протоколов.
*1--M2M протокол должен быть бинарным
Это сделает трафик компактнее и проще в обработке на стороне firmware.
*2--У пакета должна быть преамбула (синхробайты)
Преамбула нужна, чтобы программный конечный автомат приема мог выхватывать пакеты из потока байтов.
*3--Преамбула должна быть параметризируемая
Преамбулу следует параметризировать так как должна быть предусмотрена возможность туннелирования пакетов одного и того же протокола. То есть матрешка пакетов из одного и того же протокола. Это особенно полезно когда физика трансивера за раз может отправить только N байт, а сам пакет, например, M=2N байт. При этом получается N<M. В этом случае маленькими пакетами передается большой пакет как поток байтов.
*4--Должен быть порядковый номер пакета (Sequence Number)
Это позволит на стороне приемника определить потерю непрерывности потока. Порядковый номер пакета это также требование стандарта ISO-26262
*5--Должно быть поле, которое отвечает за длину полезных данных
Это позволит сделать протокол универсальным и сделает возможность передавать данные разной длинны.
6--Передавать многобайтовые поля в заголовках следует в Big Endian (optional)
Так проще анализировать сырые пакеты глазами в HEX редакторе или на осциллографе. Даже если микроконтроллер Little Endian, то производительность современных микроконтроллеров достаточно высока, чтобы инвертировать байты для всех типов на лету.
*7--В пакете должна быть контрольная сумма CRC
Это позволит проверить, что пакет не повредился в процессе передачи из-за радиопомех или дребезга контактов.
*8--Контрольная сумма должна быть в конце пакета
Это позволит проще вычислять СRС, захватывая как данные так и заголовок.
9--Должен быть идентификатор типа полезных данных(optional)
Это позволит приемнику быстрее понять как интерпретировать полезные данные внутри пакета.
*10--Должно быть подтверждение принятого пакета (Ack)
Так Master Node(а) поймет, что Slave Node(а) точно съела пакет
*11--Должна быть повторная отправка в случае отсутствия подтверждения (ReTx)
Это повысит надежность передачи данных.
12--В заголовке должна быть TimeStamp отправки пакета миллисекундной точности (optional)
Временная отметка внутри пакета нужна для отладки хронологии диалога и оценки быстродействия обработки. Есть аппаратные способы прослушивать трафик в той же RS485. По логу надо уметь анализировать тайминги. Сортировать хронологию, оценивать паузы.
*13--Спецификацию протокола следует делать в формате общих электронных таблиц
Делать спеки протоколов в *.doc или *.pdf это уже архаично. Электронные таблицы идеально подходят для представления структуры пакетов. Электронные таблицы как будто бы и созданы для представления пакетов. Любой пакет это по факту обыкновенный массив. В электронных таблицах можно сортировать пакеты, быстро искать нужный пакет, раскрашивать поля, проверять полноту, находить свободные идентификаторы, и даже авто генерировать парсеры в виде С-кода для синтаксического разбора пакетов.
*14--Должно быть шифрование PayLoad(а)
В случае ответственных задач имеет смысл добавить шифрование полезных данных пакета ,чтобы нелегальные узлы не могли наблюдать за работой системы и подключать к общей шине свою шпионскую телеметрию и тем более телематику для совершения диверсий.
*15--Пакетная синхронизация должна производится по преамбуле
Раз приняли преамбулу и CRC валидная значит пришел новый пакет. Так прошивка сможет выделять пакеты из потока байт в UART.
*16--Пакетная синхронизация должна производится также по TimeOut(у)
Также можно делать пакетную синхронизацию по TimeOut(у). То есть после продолжительного молчания на шине приемник просто сбрасывает конечный автомат приема в первоначальное состояние и ожидает новой преамбулы от нового пакета.
*17--Должен быть периодический Hello пакет для каждой node(ы) (keep alive messages)
Мастер должен периодически опрашивать каждую Node(у) даже если нет PayLoad для этой Node(ы). (Требование ISO-26262). Это позволит убедиться, что есть link. То есть что провода не оторвались, софт не завис. В случае пропадания link(а) сгенерировать событие аварии и предложить предпринять меры по ремонту сети. В UWB это, например, называется Blink пакеты.
18--В пакете должен быть адрес отправителя и адрес получателя
Это как на почте на конвертах. Правило здравого смысла. Если в пакете есть адреса, то можно делать топологию общая шина, отвечать может только тот кому этот пакет принадлежит.
19--Сторожевой таймер на потерю соединения
Каждый раз когда приходит пакет надо обнулять программный сторожевой таймер, который работает для этого конкретного соединения. Этот таймер считает вверх. Если сторожевой таймер досчитал до определенного таймаута, то надо выдать в консоль предупреждение, что возможно что-то не так с link(ом).
Вывод
Это основные требования к выбору кибернетики очередного бинарного протокола передачи данных. Протокола для M2M взаимодействий. Вообще не надо изобретать очередной проприетарный протокол. Воспользуйтесь спецификацией любого уже существующего канального протокола. Вероятно вам подойдет ModBus, Can, DLMS, RDS, UBX, xModem, EtherCAT. Стандартизированный протокол добавит вашему устройству совместимость с другим оборудованием и софтом. Можно будет взять программную реализацию с GitHub и просто покрыть ее своими модульными тестами.
Если вам известны еще особенные атрибуты хорошего канального протокола передачи данных, то пишите их в комментарии.
Также пишите про те стандартизированные канальные протоколы с которыми вам приходилось сталкиваться и делитель впечатлениями от работы с разными протоколами канального уровня.