Самодельный протокол передачи данных


• О проекте
• Обратная связь
• Полезные ссылки
• Полезные программы
• Друзья сайта


Последние комментарии

Аби: Подключение микроконтроллеров к шине RS-485
написано просто о...

Анатолий: Джойстик для денди на stm32
Автору 100500 рес...




           

Библиотека для AVR





AXLIB Генератор





Помощь сайту


				

Самодельный протокол передачи данных

	
	
	

Дата: 1 Февраля 2014. Автор: Алексей

	
	
Всем привет!
Пример работы ПК и слева.
Пример работы мастера и два слева.
Программа виртуальный Мастер для отладки протокола MHBUS.

Сегодня я решил поделится своим мнением на тему самодельных протоколов передачи данных по любой линии передач. Возник у меня вопрос о мониторинге температуры комнат и улицы в загородном доме с дальнейшей передачей на сервер по GSM. Вот тут и встал вопрос как сделать сбор температур. Особо не задумываясь было принято решение в использовании датчиков температуры DS18S20. Простота, быстрота и точность вполне устраивает. Работает датчик по шине 1-Wire что устраивало опять же простотой и длинна аж до 300 метров. Но при дальнейшем развитии проекта потребовалось задавать пределы температур с дальнейшей обработкой по превышению. С учетом того что датчиков много и обрабатывать данные по каждому слишком расточительно (мне так показалась), то решили сделать модульный датчик и обвязать по шине RS-485. Теперь каждый датчик состоял из DS18S20, MAX485, а управляла всем этим ATmega8. Ну с железом разобрались, теперь протокол. Сначала я полез на форумы поспрашать что по чем. Все в один голос MODBUS. Скачал и распечатал полный мануал на этот протокао, изучил и понял что в моем случае это из пушки по воробьям. Дай думаю спрошу, может кто что свое наваял по проще. Ага, ща... Я не нашел ни одного решения. То ли никто не пробовал, то ли жалко поделиться. Короче решил я придумать свой велосипед и поделится с Вами. Если возникнут какие-то вопросы, пожелания, критика принимаю все. Это мое первое написание протокола.
Ну... Поехали.

Для хохмы я его назвал MH-BUS. MH - это по тому что Мой Домашний :) Первым делом я решил избавится от плавающей длинны пакета, то есть длинна данных имеет фиксированную длину. Это связано с тем, что в CodeVisionAVR размер переменной типа float равна 4 байта. А с учетом того что передаем таким типом переменных в основном температуру, то длинна передаваемых данных всегда равна 4 байта. В те же 4 байта можно смело запихнуть long int и передавать число аж 4294967296. С данными разобрались, а теперь нужно как-то объяснить контроллеру что с ними делать. Для этого используется один байт с кодом команды. По этому коду ведомое устройство всегда знает что ему надо делать. Если ведомое устройство занимается только одним делом, например меряет какой-то параметр, то по команде оно может передать измеренное значение. А что делать если устройство модульное и имеет много параметров? Для этой цели существует один байт регистра(ну прям как в MODBUS). Данный байт передает номер регистра в котором ведомое устройство хранит значения какого-то параметра. Ах да, как же ведомое устройство узнает что команда передана именно ему? А вот за это отвечает еще один байт и в него нужно записать адрес. И наконец весь этот огород обрамляют два байта, стартовый и стоповый.
Теперь давайте взглянем на картинку пакета.

Формат пакета

Теперь все по порядку.

    1) Старт байт:      Всегда принимает значение 0xAA.
    2) Адрес:             Адрес ведомого устройства.
    3) Команда:         Команда ведомому устройству.
    4) Регистр:           Адрес регистра где ведомое устройство хранит параметр.
    5) Данные:           3-й байт данных.
    6) Данные:           2-й байт данных.
    7) Данные:           1-й байт данных.
    8) Данные:           0-й байт данных.
    9) Контрольная:   1-й байт контрольной суммы.
    10) Сумма:           0-й байт контрольной суммы.
    11) Стоп байт:      Всегда принимает значение 0x55.

Стартовый байт выбран 0xAA не случайно. Если его посмотреть в бинаре то получем b10101010. Это для защиты от помех. Создать такую наводку достаточно сложно, а исказить легко. Стоповый байт 0x55 это побитное отрицание стартового b01010101. Таким образом контроллер всегда знает где начало пакета, а где конец. Адресный байт может принимать значение в диапазоне от 0x01 до 0x33. Это дает возможность подключить к шине 50 ведомых устройств. Нулевой адрес выбран свободным и является широковещательным. То есть его принимают все ведомые устройства не зависимо от своего адреса. Это нужно например для подачи команды перезагрузки ведомых устройств или если устройства впервые появились на линии и имеют адреса 0xFF (это обязательный адрес свежеподключенного устройства для избегания коллизий адресного пространства). Если подключить сразу несколько устройств с адресами 0xFF то как они узнают кому пришла команда на смену адреса? Вот здесь и нужна широковещательная передача. По нулевому адресу передаем команду на изменение адреса ведомого устройства, а в байтах данных передаем его идентификационный номер. Помните сколько вариантов можно переслать. Я принял такое решение для того чтобы не залезать в настройки ведомых устройств. Просто подключили к мастеру и указали ИН. Все, дальше мастер сам разберется какой адрес дать. После адреса идет байт команд. Диапазон команд может быть от 0x21 до 0xFF. О командах мы поговорим чуть позже. Далее идет байт регистра. Диапазон регистров от 0x01 до 0xFF. Нулевой застолбил на всякий случай. Далее четыре байта это данные. Записываются они шиворот на выворот, то есть слева на право но с последнего байта к первому. Связано это с конвертацией float в массив из четырех char. Далее идут два байта контрольной суммы. Они также идут задом на перед. Как считать контрольную сумму. Не мудрствуя лукаво я решил убить трех зайцев одним выстрелом. Первый заяц это защита данных, второй простота подсчета и третий минимальная нагрузка на процессор. И так, расчет ведется следующим образом: берем значения байта адреса, байта команды, байта регистра, четыре байта данных и просто алгебраически складываем их. Почему два байта? Если сложить семь данных байт со значениями 0xFF то получим число 0x06F9. Как видно оно вполне влезает в два байта.

Теперь давайте разбирать команды.

0x21 - Изменить адрес ведомого устройства с текущего на новый. Новый адрес 
          передается в байте регистра. (при широковещательной передаче ведомое
          устройство выполняет команду при совпадении ИН передаваемого со своим)

0x22 - Вернуть значение температуры.(только если датчик измеряет один параметр)

0x23 - Вернуть значение давления.(только если датчик измеряет один параметр)

0x24 - Резерв (для каких-нибудь еще датчиков)

0x25 - Резерв (для каких-нибудь еще датчиков)

0x26 - Резерв (для каких-нибудь еще датчиков)

0x27 - Резерв (для каких-нибудь еще датчиков)

0x28 - Резерв (для каких-нибудь еще датчиков)

0x29 - Резерв (для каких-нибудь еще датчиков)

0x2A - Вернуть параметр находящийся по адресу регистра

0x2B - Вернуть дискрет находящийся по адресу регистра

0x2C - Вернуть байт данных

0x2D - Вернуть 2 байта данных

0x2E - Вернуть 3 байта данных

0x2F - Вернуть 4 байта данных

0x30 - Вернуть 8 дискретов подряд начиная с адреса регистра

0x31 - Вернуть 16 дискретов подряд начиная с адреса регистра

0x32 - Вернуть 24 дискретов подряд начиная с адреса регистра

0x33 - Вернуть 32 дискретов подряд начиная с адреса регистра

0x34 - Записать параметр находящийся по адресу регистра (например предел)

0x35 - Записать дискрет находящийся по адресу регистра

0x36 - Записать байт данных

0x37 - Записать 2 байта данных

0x38 - Записать 3 байта данных

0x39 - Записать 4 байта данных

0x3A - Записать 8 дискретов подряд начиная с адреса регистра

0x3B - Записать 16 дискретов подряд начиная с адреса регистра

0x3C - Записать 24 дискретов подряд начиная с адреса регистра

0x3D - Записать 32 дискретов подряд начиная с адреса регистра

Для начала я считаю что этих команд достаточно. Самое важное то что ведомое устройство обязано вернуть ответ на любой запрос, даже если ответ не требуется. При команде не требующей ответа, ведомое устройство возвращает обязательно свой адрес, номер команды и номер регистра если использовался, а в байты данных записывает любую ахинею для более точного формирования контрольной суммы. Это требуется для предотвращения зависания мастера на случай падения ведомого. Если после запроса ведомое устройство не отвечает некоторое время, то считается мертвым и выкидывается из опроса. Так же можно сформировать об этом какую-нибудь ошибку. Но это уже на Ваш вкус.

Теперь самое интересное. Ошибки. Ведь не всегда все хорошо, ведомое получило запрос на возврат температуру, а сам измеряет давление. Или датчик помер и получить параметр не возможно. Что делать? Нужно об это сказать мастеру. Вот для этих целей придумал номера ошибок. Номера ошибок при ответе ведомого модуля записываются в байт регистра.

0x01 - Несоответствие стопового байта со значением 0x55

0x02 - Несовпадение контрольной суммы

0x03 - Неприемлемая команда

0x04 - Отсутствие в модуле запрашиваемого регистра

0x05 - Неисправность измерительного датчика в модуле

0x06 - Невозможность произвести запись параметра

0x07 - Экзотическая ошибка. Описание ошибки записывается в байты данных.

0x08 - Несоответствие стартового байта со значением 0xAA

0x09 - Модуль не отвечает (по таймауту)

0x0A - Несовпадения адреса

0x0B - Адрес вне диапазона

0x0C - Адрес не записался

0x0D - Несовпадение ID устройства с передаваемым от Мастера

0x0E - Битый пакет

0x0F - Резерв

Ну с теорией покончили, давайте посмотрим на это безобразие в живую. Я тестил на все том же датчике температуры. Для начала давайте рассмотрим пакет запроса. Слева направо.

Запрос

Первый байт 170 в HEX 0xAA. Знакома? Правильно старт байт. Второй байт 1 это адрес. Третий байт, плохо видно, это двойная кавычка. В HEX выглядит как 0x22. Команда. Четвертый байт 0. Мы же хотим температуру узнать и все. Байты данные раны нулю, так как мы ничего не передаем. Можно записать туда все что угодно для усиления защиты :) Девятый байт это решетка в HEX 0x23. Десятый байт ноль. Одиннадцатый байт буква U в HEX 0x55. Правильно, стоп байт.

Давайте ради интереса проверим контрольную сумму. Если вспомнить про задом наперед то контрольная сумма равна 0x0023. Теперь складываем адрес, команду, регистр и данные. 0x01+0x22+0x00+0x00+0x00+0x00+0x00 = 0x23. Ух ты! Совпала. Теперь давайте рассмотрим ответный пакет.

Ответ

Первый байт 170. С ним все ясно. Второй адрес 1. Третий команда 0x22. Это хорошо, пакет дошел без ошибок. Четвертый ноль. Данные. Шиворот навыворот склеиваем байты. А+208+0+0 и переводим в HEX. Это будет 0x41D00000. Контрольная сумма равна 0x0134 (мой логический регистратор выводит значения в виде ASCII кодов, а если нет такого кода, то выводит в десятичном виде. Поэтому 4 на картинке это 0x34 она без одинарных ковычек. Считаем 0x01+0x22+0x00+0x00+0x00+0xD0+0x41 = 0x134. Ура! Совпало.

И самое интересное, че он нам прислал.
Для этого давайте переведем из HEX во float. Это можно сделать здесь он-лайн :) Впишем наш 0x41D00000 и жмем Convert to float.

Перевод из HEX во float

Уау! 26.0 градусов по Цельсию. А че кажет ЖК-дисплей?

Вывод температуры на ЖК-дисплей

А это весь зоопарк в сборе.

Все в сборе

Пример работы ПК и слева.
Пример работы мастера и два слева.
Программа виртуальный Мастер для отладки протокола MHBUS.

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



Евгений    09.07.14

Интересная информация, хотелось бы посмотреть на исходники этого протокола. Сам с аврками еще мало общался, но есть такая задачка, в которой нужно передавать тактовый импульс и данные по одному проводу с помощью RC-цепочки. Вообще интересна реализация "самопальных" протоколов и т.д Не могли бы Вы поделиться таковой? (в том числе и Вашим протоколом, если можно) Адрес моей почты - rulo90@mail.ru Спасибо!

Алексей    09.07.14

У меня хард умер и старые проекты с ним. Если найду этот проект, то спину.

Юрий    01.07.15 00:36

Сделай статтю, или покажи на примере как соединить два микроконтроллера AVR на расстоянии до 400 м, по шине rs485. Буду очень признателен.

Алексей    01.07.15 08:07

Да. Надо написать. Блин я и забыл про 485. Сделаем.

Юрий    07.07.15 12:43

Заинтересовал Ваш протокол передачи данних, покажите как на примере реализовать его. Спасибо.

Алексей    07.07.15 17:59

Хорошо. Я попробую написать библиотеку для протокола к ведомому и ведущему устройствам.

Антон    24.10.15 13:38

Отличная статья. Когда мы сможем увидеть исходники или библиотеку передачи? Заранее благодарер

Алексей    24.10.15 19:46

В разработке. А если честно, то лень. Начал, половину сделал и забросил. Надо раскачаться.

CrazyPit    21.02.16 23:22

советую использовать модбас.. хотябы функции чтения записи. этого достаточно. пишется просто да и исходников полон интернет. берете 2 функции и все.. за таймингами можно не следить(вот и у вас уже свой доморощенный протокол)

Алексей    22.02.16 10:38

Так этот уже готов. А модбас груженый.

e_gorka    12.08.16 06:46

А если в данных или в контрольной сумме окажется 0xAA или 0x55?

Алексей    12.08.16 08:37

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

Юрий    18.08.16 11:15

Алексей добрый день! Как с Вами можно связаться! У меня к Вам множество вопросов, может работа Вам подойдет? Мой телефон для связи: 89255071195 www.steamwash.ru Мы занимаемся распределенным датчиком температуры, и у нас стал вопрос передачи температурных данных по протоколу Modbus/

Юрий    18.08.16 11:16

Ой не тот адрес записал:WWW.OPTICALPATH.RU

Алексей    18.08.16 11:57

А при чем здесь MODBUS и я? Я MODBUS не использую в своих проектах из-за его загруженности. Это промышленный протокол и для его реализации под МК уже написано куча библиотек. Не понимаю суть вопроса. А вопросы можно писать в обратную связь.




Чтобы вставить ссылку используйте форму вида[url]http://www.адрес.ru[/url][text]текст ссылки[/text]
Чтобы вставить код используйте форму вида[code]код[/code]

Имя:   





  







Рейтинг@Mail.ru Яндекс.Метрика