Как бы сделали "Вы"? Обработка передачи/приёма пакетов через COMM.
- Войдите на сайт для отправки комментариев
Всем день добрый и с наступающим.
Накорябал временный проектик для общения компютерной мышки с ардуинкой через Serial.
Почему временный? Да потому что пока всё очень сыровато. Да и весь проект пишится как маленькая "не всегда нужная" програмка.
Почему именно через Serial, а не как-нибудь по изящьней? Втом то и фокус, хочу через Serial.
Мой вопрос в следующем:
Гляньте пожалуйста взорким взглядом, может есть места где следует поступить по разумнее.
Вот так вот формируется пакет с данными на стороне JAVA:
private int[] getData(Point point, byte action){ /* * FF - Mark (Package Start) * 'M' - "Mouse" * 'A' - "Action" * a - actions (10,11,12,13,14,15,16,17,18,19,20) * 'X' - Mark (X-Coordinates) * x - X-Coordinate * 'Y' - Mark (Y-Coordinates) * y - Y-Coordinate * '\n'- Mark (Package End) */ return new int[]{ 0xFF, 'M' , 'A' , action, 'X' , (char)point.getX(), 'Y' , (char)point.getY(), '\n' }; // Datenpaket "9-Byte" }
Вот так получаю данные на ардуинке:
const byte DRAGGED_ACTION = 10; const byte MOVED_ACTION = 11; const byte WHEELMOVED_UP_ACTION = 12; const byte WHEELMOVED_DOWN_ACTION = 13; const byte CLICKED_ACTION = 14; const byte PRESSED_ACTION = 15; const byte RELEASED_ACTION = 16; const byte ENTERED_ACTION = 17; const byte EXITED_ACTION = 18; const byte ENABLE_ACTION = 19; const byte DISABLE_ACTION = 20; const byte MARK_BEGIN = 255; const byte MARK_M = 'M'; const byte MARK_A = 'A'; const byte MARK_X = 'X'; const byte MARK_Y = 'Y'; const byte MARK_END = '\n'; byte maStep; // Mouse Action Schrit byte maArray[3]; // 0 = Action, 1 = XPos, 2 = YPos byte tempByte; void setup(){ maStep = 0; // Noch nichts gefunden Serial.begin(9600); } void loop(){ while(Serial.available()>0){ tempByte = Serial.read(); if(tempByte == MARK_BEGIN && maStep == 0){ maStep = 1; // Marke gefunden }else if(tempByte == MARK_M && maStep == 1){ maStep = 2; // 'Mouse' gefunden }else if(tempByte == MARK_A && maStep == 2){ maStep = 3; // 'Action' gefunden }else if(maStep == 3){ maArray[0] = tempByte; maStep = 4; // Action-Wert gespeichert }else if(tempByte == MARK_X && maStep == 4){ maStep = 5; // 'X' gefunden }else if(maStep == 5){ maArray[1] = tempByte; maStep = 6; // X-Wert gespeichert }else if(tempByte == MARK_Y && maStep == 6){ maStep = 7; // 'Y' gefunden }else if(maStep == 7){ maArray[2] = tempByte; maStep = 8; // Y-Wert gespeichert }else if(tempByte == MARK_END && maStep == 8){ doAction(maArray[0], maArray[1], maArray[2]); maStep = 0; // Paket gefunden zu Anfang gehen }else{ // Ungültiger paket maStep = 0; // Nichts gefunden Serial.print("UNKNOWN PACKET CONTENT AT STEP: "); Serial.print(maStep); Serial.print(" CHAR: "); Serial.write(tempByte); Serial.println(); } } } void doAction(byte a, byte x, byte y){ switch(a){ case DRAGGED_ACTION: Serial.print("DRAGGED_ACTION "); break; case MOVED_ACTION: Serial.print("MOVED_ACTION "); break; case WHEELMOVED_UP_ACTION: Serial.print("WHEELMOVED_UP_ACTION "); break; case WHEELMOVED_DOWN_ACTION: Serial.print("WHEELMOVED_DOWN_ACTION "); break; case CLICKED_ACTION: Serial.print("CLICKED_ACTION "); break; case PRESSED_ACTION: Serial.print("PRESSED_ACTION "); break; case RELEASED_ACTION: Serial.print("RELEASED_ACTION "); break; case ENTERED_ACTION: Serial.print("ENTERED_ACTION "); break; case EXITED_ACTION: Serial.print("EXITED_ACTION "); break; case ENABLE_ACTION: Serial.print("ENABLE_ACTION "); break; case DISABLE_ACTION: Serial.print("DISABLE_ACTION "); break; default: Serial.print("UNKNOWN_ACTION "); } Serial.print("X: "); Serial.print(x); Serial.print(" Y: "); Serial.println(y); }
Ардуиновский код пока эксперементальный. Потом хочу переписать Serial как тут и добавить туда Listeners для обработки MouseEvents которые будет бросать JAVA через Serial. Тоесть будит обычный Serial с функциями например: onMouseAction(), onMouseMotion(), onMouseWheel(). Тут думаю прейдётся по пыхтеть чтобы соединить эти два чуда. На крайний случай напишу отдельную библиотеку для мыхи на базе Serial.
Мне бы хотелось услышать от вас советы как по лучше сформировать передачу и приём пакетов. На данный момент всё работает так как я и предстовлял (хотел) . Но как всегда, есть какие-то камни каторые я просмотрел "на радостях" и в итоге всё загнётся из за какой-нибудь мелочи
.
Функция void doAction(byte a, byte x, byte y); эмитирует бросание Event(ов). Но вот самое интересное это строки с 29 по 61. Не сильно ли я перебощил с IF-оми? Ведь если я каждый раз буду проверять таким способом "не идёт ли нужный мне покет" то Serial совсем медленно работать будит.
Для начала, со стороны джавы, я бы добавил "небольшое вранье". В случае координат X,Y равных 255, отправлял 254 (и при 254 - тоже 254). Пусть у нас "рабочий стол" будет на одну точку меньше.
Зато это даст вам уверенность что 0xFF - никогда не попадется посреди пакета данных. И что у вас не будет "не правильно начало пакета определили".
Тогда отпадает и необходимость в "завершающем маркере".
Можно дейстсовать по логике: ждем FF, потом читаем 7-байт самих данных :)
Но вот самое интересное это строки с 29 по 61. Не сильно ли я перебощил с IF-оми? Ведь если я каждый раз буду проверять таким способом "не идёт ли нужный мне покет" то Serial совсем медленно работать будит.
Это не интерестно, это ужасно :) Дело даже не в том что "медлено", а в том что такое код сопровождать-поддерживать - взрыв мозга.
Почему нельзя, при чтении следующего байта просто делать maStep++? Зачем эти магически числа maStep=... руками хардкодить? А потом еще, руками-же, вписывать куда в maArray положить? Опять "магические числа".
Зачем вам MARK_X,MARK_Y? Разве у вас может быть ситуация, что Y прийдет раньше X? Нет. Нам вообще сам пакет данных можно сократить до трех (плюс один стартовый). первый байт - имя действия, второй и третить - параметры действия.
Тогда все ваши if-ы упростяться до
maArray[maStep++]=tempByte;
Далее. Использовать массив, в данном случае - тоже не очень красиво. Упомни еще потом, что под каким индексом в нем лежит.
Гораздо лучше использовать что-то "с говорящим именем", для этого в C существую структуры.
Кроме того, у класса Serial, есть метод http://arduino.cc/en/Reference/StreamReadBytes
Значит "все чтение", в итоге (если со стороны джавы единственный посылаемый маркер MARK_BEGIN) у нас сведется к чему-то такому
Как видите у нас нет индексов, четко видно что именно мы передаем в doAction. Не надо рыскать по коду для выяснения "что означает maArray[1]?". Согласитесть что data.x - более понятно :)
Для начала, со стороны джавы, я бы добавил "небольшое вранье".
Так как я не весь JAVA код показал, вы и пришли на эту мысл. Я на данный момент ограничиваю byte до 253, тоесть у меня 254 и 255 не могут быть отправленны как координаты. Но всё ровно спосибо.
Это не интерестно, это ужасно :)
Этого-то я и боялся
. Я пока ещё не работал с передачей данных пакетами, по этому так страшно и получилось.
В покете так много "лишнего" по тому что, я хотел избавится от "Лжепакетов".
Допустим я минимирую пакет до 4-Byte т.е. (Mark(255), Action(...), XPos(...), YPos(...)) => (255, 10, 125, 125) я думаю что здесь не достаточно чётко сформулирован пакет. Что если после Mark проскользнёт какой-нибудь байт который был отправлен чем-нибудь другим. Мой код схавает это как "UNKNOWN_PACKAGE" но минимальная версия посчитает его правельным. А это не верно. Так же проблеммы со скоростью передачи данных: если скорось сильно медленная и оброботчик не справляется. То при отрпвки следующих покетов:
255 10 125 125
255 11 0 0
255 13 98 88
Возможно что до оброботчика дайдёт только один-"лжепакет" 255 10 11 13.
По этому я и добавил маркеры (255 'M') которые говорят что идёт пакет от мышки, и на протяжении сборки пакета каждый блок помечен марками (A-Action, X-Xpos, Y-Ypos), а для полной уверенности я и добавил maStep чтобы отследить правельную полседовательность блоков и в случае если кто-то вклинился или байты потерялись то я не получу не верный пакет. А все те пакеты которые я получу я могу быть уверен на 98% что они верные.
А насчёт последнего байта '\n' в пакете, дак я его добавил просто для эстэтики, при дебаге на мониторе приятней выглядит.
В библиотеке Firmata они передают данные в 7 битах, т.е. маются с перепаковкой данных, тогда 10 бит пересылается как 7 младших бит, затем три старших, т.е. передают одно слово (2 байта). На приемном конце просто собирают обратно. Зато гарантировано, что 255 никогда не появится.
Кстати, возможно это поможет, можете посмотреть исходники, они входят в стандартный комплект ArduinoIDE. Если что, я в них собаку съел ;) накопался до и больше.
Они реализовали MIDI протокол только со своими данными и дополнениями. Либа, кстати, вполне интересная, возможно будет полезна, хотя бы посмотреть исходники.
Да к стати, забыл написать. Поралельно к пакетам от мышки, возможно посылать другие данные, каторые с мышкой совсем не связанны. По этому и страх о мусаре в пакетах. Т.е.
if(Serial.available() && Serial.read()==MARK_BEGIN){
это не 100% мышка!
>Что если после Mark проскользнёт какой-нибудь байт который был отправлен чем-нибудь другим
Что значит "чем нибудь другим"? Чем? Две програмы, одновременно не могут открыть Serial порт. Сделовательно прорамно "кого-то другого" быть просто не может (разве что вы сами начнете из джавы писать в несколько потоков, но с этим винегретом, не допускать что-бы несколько потоков писали, нужно разбиратся на стороне java).
Может "кто-то намусорить" только аппаратно (еще кто-то на эту линию подключен, пробкотрон рядом работает и т.п.), но... с обычным кабелем - да почти нет шансов. Ниразу "мусор" не видел, даже на 115200, не то что на 9600.
Во вторых, а чем вам спасут, при этом, куча маркеров? Если кто-то "срет в канал", так ведь он не только вклинится может, но и "исказить". Маркеры пройдут, а X,Y - повредятся.
В этих случаях, вместо фиксированногопередают еще и "контрольную сумму". Самый простой вариант - перемножаем все байты данных между собой по XOR. И передаем "что получилось". По принятии - опять-таки вычисляем контрульную сумму "того что приняли", если совпало - значит, с большой долей вероятности, пакет принят без повреждений (вставок, искажений и т.п.)
По этому я и добавил маркеры (255 'M') которые говорят что идёт пакет от мышки
Да не проблема, принцип сохраняется, ну добавте еще одно поле в структуру. Например source его назовите. И посылайте его первым. То есть это не маркер, а "еще одно поле данных".
Окай, я понял что не достаточно разобрался с принципом работы Serial.
Я накарябал на JAVA Камуникатор для тестов Arduin(ки) естественно данные как получать так и передовать можно различными спосабами. Толко для отправки у меня 4 варианта (1-Мышка, 2-с помощбю поля, 3-быстрая отправка, 4-из файла) и всё это может отправлять одновременно.
Прейдётся отказаться от возможности передавать данные в перемешку, тогда много ограничений отвалится да и проблем с пакетами-мыши не будит.
Прейдётся отказаться от возможности передавать данные в перемешку, тогда много ограничений отвалится да и проблем с пакетами-мыши не будит.
Что-бы от чего-то отказатся - нужно это иметь :) А передавать данные одновременно вы в любом случае не можете. Даже с вашими маркерами вы, в лучшем случае, просто выкидываете данные которые "перемешались". То есть передача "останавливается", когда начинают говорить одновременно все, то стоит неразборчивый "гвалт" :)
Со стороным java у вас есть два выхода. Либо организовывать какой-то lock сериала. Вообщем стандартные меж.поточные блокировки. По принципу "кто первый схватил, того и тапки" :) (главное что-бы не забыл потом отдать другим попользоватся).
Либо организовать какую-то очередь-событий. Все ваши девайсы сами не отправляют, а помещают "в очередь" эти события. И где-то в бекграунде, висит отдельный класс-поток. Который посматривает на очередь, если она не пустая. Берет событие и отправляет его в Serial. После чего убирает его из очереди.
То есть с Serial работает только какой-то специальный класс-посредник. А все остальные только просят его "вот отправь это письмо, когда у тебя будет время" :)
IMHO второй вариант лучше. Тогда можно, если событий много, реализовать что-то типа "выкидываем из очереди не отправленые события, если у нас пришла толпа новых". Или, например, "если в очереди есть несколько событий типа mouseMove, то отправляем только "самое свежие", а остальные в корзину как "потерявшие актуальность".
Что-бы от чего-то отказатся - нужно это иметь :) А передавать данные одновременно вы в любом случае не можете.
С "одновременно" я имел в виду "в перимешку". Тоесть не обязательно будут отправляться данные мышки.
Вот к примеру я захотел с помощью JAVA-Комуникатара управлять роботом. Колёсами кручу мышкой, лампачками моргаю "быстрыми кнопками" и так далее. Плюс ко всему что бы не терялась возможность обыкновенного Serial-монитора (отправка чистых данных без всяких меток).
Благодорю вас за идею с обработчиком отправленных сообщений. Очень понравелось. Буду реализовать.
Что-бы от чего-то отказатся - нужно это иметь :) А передавать данные одновременно вы в любом случае не можете.
С "одновременно" я имел в виду "в перимешку". Тоесть не обязательно будут отправляться данные мышки.
Я тоже имено эту ситуацию и имел ввиду. При этом я утверждаю что "в перемешку" и "вообще ничего не посылаем" - это одно и тоже. Поэтому "перемешки" нужно избегать любыми способами.
Вот, предположим у нас есть два таких валидный пакета пакета:
{255,'M', 4,5}
{255,'K', 7,8}
в ответ на них вызовутся функции doMouseAction(4,5) и doKeyboardAction(7,8)
Все хорошо. А теперь "перемешаем" их (и я специально поменяю значения параметров, что-ы в по ним не могли узнать "где-что") и пошлем такой пакет {255,'M',255,'K',12,8,60,54}
Вот даже не "ардуина", а вы сами можете распознать такой пакет? Скажете однозначно с какими параметрами должны быть вызваны doMouseAction и doKeyboardAction? Нет. У нас получается куча возможных вариантов что мог бы означать такой "винигрет":
doMouseAction(12,8);doMouseAction(60,54);
или doMouseAction(12,60);doMouseAction(8,54);
или doMouseAction(12,54);doMouseAction(8,60);
И выбрать какой-то из этих вариантов - мы не можем. Следовательно единственно что мы можем сделать это "срыгнуть" это блюдо. Тупо проигнорировать его. Ничего не делать. То есть такая перемешка эквивалентна "нам вообще ничего не послали".
Поэтому, со стороны джавы, будет лучше даже если "второй говорящий" вообще промолчит и потеряет свое событие. Тогда хоть для первого мы команду выполним.
------
Если с "очередями событий" не выйдет и будет слишком сложно. Можете попробовать другой подход. Есть некий менеджер. У которого есть "микрофон" (Serial). И он по очереди, дает его всем говорящим Мышке, Полю, Джойстику и т.п. Дает каждому Serial и со словами "скажи туда, если тебе есть что сказать, и верни его мне". Вообщем теледебаты :) Политиков желающих поговорить - много, микрофон - один. И ведущий который передает его по кругу.
Или, все-таки, "очередь". Но можно не "событий", а каких-то делегатов. Мышка, Поля, Джостик, помещает какие-то делегаты в очередь (там может быть посылка в Serial), а потом кто-то внешний выполняет эти делегаты "в порядке очереди".
- для начало, что мы имеем: у нас есть два вида данных 1) от мышки который идёт пакетами 2) общие которые посылают поля и файл. Во втором варианте нет ни каких пакетов т.е. это может быть .JPEG или .TXT файлик который мне захотелось передать.
- ситуация: допустим есть экранчик который должин получать через КОМ картинки и вертеться на сервах показывая результат всем вокруг себя, управляясь мышкой.
- так как со стороны JAVA я хочу получить универсальный комуникатор т.е. не специалезированный на конкретную проблемму, то данные я делю на два вида (1) и (2) "описанные выше".
- подход к проблеме: Ардуина знает, что через Serial к ней идёт картинка (хаотичные данные) но так же должна уметь из всего этого хаоса отфильтровать данные мышки. Как я понимаю один "депутат" шлёт весь файл, то логичней было бы другим "депутатом" молчать. Тоесть мы находимся в идеальном мире, где все друг друга уважают и не перебивают. "На данный момент у меня в случае если один замешкался, то другой может насрать"- надо избавиться от этого. Далее проблемма, я не могу исключить что в картине не будут находиться "лжепакеты", не важно как я это делаю вашим (экономным) или моим (расточительным) способом. Но вот шанс того что пакет {255,'M', 4,5} в картинке проскочет очень велик, а вот с таким пакетом {255,'M','A',10,'X',3,'Y',10} уже ниже.
- как я себе представляю очередь: Класс очереди имеет Thread который постоянно шлёт данные которые ему были переданны. т.е. есть лист в который помещаются данные (1) и (2). Данные можно поместить только целеком (111111111111111111111111111111) (222222) но не в перемешку (1112111112212111121)(212121). После чего клас очереди постепенно отсылает "чистенькие" данные, а мне на стороне ардуинки нужно только отследить пакеты от мыхи и бросить эвент. Всё что не мышкины пакеты будут пониматься как картинка.
Во втором варианте нет ни каких пакетов
ну так сделайте что-бы были. Что вам мешает разбить файл на прорции и каждую из них считать "пакетом"?
Пусть будет два вида пакетов mouseData и imagePart
Сами пакеты - смешиватся не должны. Но чередоватся - могут.
Тогда у вас пакеты mouseData смогут "протискиваться" даже во время передачи большого файла. Предположим мы разбили файл на пять частей. Причем во время его передачи у нас произошли три события мышки:
mouseData1, imagePart1, imagePart2, mouseData2, imagePart3, mouseData3,imagePart4,imagePart5
Кроме того, это там может пригодится в том, что между приемками этих пакетов, у нас ардуина еще чем-то полезным занятся может (опросить кнопки, датичики и т.п.)
imagePart дата, на вскидку должен содержать:
признак что это "последняя/не последняя часть файла".
размер (сколько байт будут посланы в этом пакете).
ну и сами байты данных.
ну так сделайте что-бы были. Что вам мешает разбить файл на прорции и каждую из них считать "пакетом"?
Вот я думал, думал над Вашим предложением на счёт пакетов и надумал оставить всё так как есть, только мышинные пакеты сделаю по компактней, как Вы предложили.
Я не хочу специализировать мой Комуникатор на конкретные пакеты, т.е. если я всё буду заварачивать в пакеты, то на стороне Ардуины нужно будит иметь соответствующую программу которая будет работать с моим комуникатором, а вот именно этого я и не хотел (ну если честно то только для мышки хотел отдельную библу, из за пакетов). Так как изночально я писал для себя более функциональный аналог Ардуиновского монитора каторый в первую ощередь работает с Serial.
Сделаю отправку с поля, с быстрой панэли и из файла как у стандартного монитора, т.е. чистые данные без всяких пакетов. А вот если нужна мыха, то в этом случае и будут слаться пакеты с инфой. (естественно на стороне ардуины накорябаю библу для обработки мыхиных пакетов).
А с перемешкой различных возможностей, "что собственно меня и в вело в заблуждение", я перевалю на пользователя. Наврядли найдутся такие проекты где надо такую кашу слать, а если и надо, то это не обязательно решать на стороне JAVA, можно непосредственно во время формерованния данных. Просто хотел как проще для будующих пользователей. (Ведь если мой комуникатор опровдает мои ожидания, не ужели я не поделюсь с остальными
)
leshak, спасибо Вам огромное за кучу идей, полюбому в жизни всё прегодиться.
ну хозяин барин. Но думаю все равно прийдетете к пакетам. Насколько они будут внутри структурированы - дело второстепенное, но сами пакеты - должны быть. :) Это же основа любого протокола :) Тем более что мышь вас уже заставила.
>leshak, спасибо Вам огромное за кучу идей, полюбому в жизни всё прегодиться.
Ну тогда ловите еще одну (ждал что постепенно дойдем до этого через пакеты, что-бы "все вообще сделать красиво"), но видимо не дождуст :)
Как избавится от портянок switch?
Помещаем наши функции обработчики в массив указателей. И имя команды (номер команды) используем в качестве индекса этого массива.
Кстати этот подход (массив обработчиков) здорово экономит нервы при написании всяких "машин состояний". Только в качестве индекса используется какой-нибудь enum. А дальше в loop() только
state_handlers[currentState]();
и все :)
переключится в другое состояние currentState=NEW_STATE; и нужный обработчик вызовится сам. Без винигретов If-воф в главном loop()
Да и мыслить "состояними" намного проще при всяких "автоматизациях крышки унитаза" ;)
Ну вот и сново стимул поднялся. Буду переписывать JAVA-Комуникатор полностью, за одно сразу под пакеты адаптирую, да и другие "косметические" неровности уберу плюс структуру в проект внести надо, а то схватился как первокласник накорябал кучу кода а через пол года сам разобраться не смогу. Хотел по быстренькому мелкую програмку написать, потом начали идеи по тихоньку подваливать, вроди "файла" и "мышки". Вот и получилось что изночальная идея конфликтовала с тем что получилось.
Со временем правдо туговато, опять учёба началась, по этому ещё не скоро задам следующий вопрос ;-). Но как Вы уже поняли, вопросы будут.
А вообще идея комуникатора очень интересная многим может пригодиться не только мне. Как победю "сырость", обязательно выложу тут готовую прогу.
Спасибо за стимул ;-)
Здраствуйте. Так как я теперь пытаюсь переписать комуникатор под пакеты, у меня появились дополнительные вопросы.
Для полной уверенности я решил к каждому пакету прикручивать hashCode размером в 1 байт.
Я подглядел как это делают Arrays.hashCode в JAVA и переписал под свою ситуацию:
Вот так вот я формирую конечный пакет:
Я думаю что для пакетов где количество элементов больше 255, hashCode может повторяться. Но не смотря ны это, я думаю что такой проверки должно быть в полне достаточно.
ИЛИ?
Сделать размер пакетов не более 255, например?
hashCode может повторяться. Но не смотря ны это, я думаю что такой проверки должно быть в полне достаточно.
ИЛИ?
Ну вообщем на то оно и хешь-функция. Это ее свойство. Называется "коллизия" (когда совпали). Незможно однозначно большее-множество проецировать на меньшее без повторений :) Вернее можно, иногда, в зависимости от своств множества, но тогда это уже архиватор получится :)
Так что "нормально". Вероятность что совпадут два хеша - ничтожно мала. В целях "контролировать канал" - этого вполне достаточно.
Даже в чем-то избыточно сложно. Я видел что обычно тупо XOR между собой всех байт. Я - не делал. На тех расстояниях что я использую дуину ни разу с потерями/помехами сталкиваться не приходилось что-бы заморачиватся ради этого.
И, все-таки, в терминах "пакетов/протоколов/каналов" все-таки скорее пользуются термином "контрольная сумма" /checksum. Но суть - таже самая. Hash - как-бы более общное понятие.
Тоесть если я Вас правельно понял, можно сокойно юзать мою идею с hash в 1 Байт?
И, все-таки, в терминах "пакетов/протоколов/каналов" все-таки скорее пользуются термином "контрольная сумма" /checksum. Но суть - таже самая. Hash - как-бы более общное понятие.
Я тоже в эту сторону смотрел, но ведь невозможно собрать контрольную сумму размером в 1 байт. Или точней сказать до определённого размера пакета можно.
Если с помощью 1 байта можно котролировать хотябы 16 байт, то было бы не плохо.
Только вот я пока не понял как мне это сделать.
И, все-таки, в терминах "пакетов/протоколов/каналов" все-таки скорее пользуются термином "контрольная сумма" /checksum. Но суть - таже самая. Hash - как-бы более общное понятие.
Я тоже в эту сторону смотрел, но ведь невозможно собрать контрольную сумму размером в 1 байт. Или точней сказать до определённого размера пакета можно.
Да это не другая сторона. Это вопрос терминологии. То что вы сделали - это контрольная сумма и есть. Просто ее можно сложной сделать, а можно простой. Можно в один байт, а можно в несколько. Баланс "надежности" и "размера/вычислительной сложности". У вас же не стоят криптографические задачи. Ну будут у вас какие-то пакеты иметь одинаковый байт контрольной суммы. Ну и что? Какая вероятность что пакет повредится таким образом что у поврежденного будет в точности такая же контрольная сумма? Да ничтожная.
В случае слабого железа, для бытовых нужд хватает даже просто контролировать "четное количество едениц в пакете или нет". То есть предполагаем что в одном пакете сразу два бита повредится не может. Если и пробралась какая-то "помеха импульс", то зацепить обычно один бит.
А в случае контольной суммы-байта - и пару бит задетектим. Что-бы сложение по XOR совпало, у вас вообще весь пакет должен быть перекоцанным что-бы совпасть. Вы же, все-таки на более менее стабильном канале работать собрались, а не радио-передатчике из фашиского плена, где одна буква из 10-ти неповрежденной приходит.
Да и сильно большие пакеты - тоже не очень хорошо. Чем больше пакет, тем больше нужно послать повторно, в случае обнаружения проблем.
Поройтесь в описаниях протоколов, особенно 90-тых годов всяких модемных (V.42, ZModem) и т.п. Эти же задачи все решались. Там не только "обнаружение ошибок", но и "востановление поврежденой инфы без повторной передачи (если процент повреждения меньше какого-то уровня) и т.д. и т.п. Да просто по протоколам какую-нибудь книгу почитайте, что-бы велосипеды не изобретать. Скорее всего уже есть что-то придуманное под вашу задачу. Сразу и "совместимым" с кем-нибудь будете. Только "а нафига оно нужно"?
Подскажите пожалуйста как мне правельно получить пакеты через Serial.
Возможные пакеты:
mousePack ровно 6 Byte
1 = 255 // Маркер указывает на начало пакета
2 = 'M' // Маркер указывает на тип пакета, в этом случае Mouse
3 = а // Действие мыши, возможные значения (0,1,2,3,4,5,6,7,8,9,10)
4 = х // Место положение мыши по Х
5 = у // Место положение мыши по У
6 = hash // Проверочный Byte построен на соединение всех предидущих Bytes через XOR
quickPack максимально 259 Byte
1 = 255 // Маркер указывает на начало пакета
2 = 'Q' // Маркер указывает на тип пакета, в этом случае Quick
3 = l // Колличество Bytes каторые относятся к чистым данным без Марок и проверочного Byte. Максимум 255 Byte
с 4 по 4+l // Полезные данные
4+l+1 = hash // Проверочный Byte построен на соединение всех предидущих Bytes через XOR
streamPack максимально 260 Byte
1 = 255 // Маркер указывает на начало пакета
2 = 'S' // Маркер указывает на тип пакета, в этом случае Stream
3 = l // Колличество Bytes каторые относятся к чистым данным без Марок и проверочного Byte. Максимум 255 Byte
4 = pos // Позиция покета относительно всего потока (Первый, Средний, Последний) Средних пакетов может быть несколько друг за другом.
с 5 по 5+l //Полезные данные
5+l+1 = hash // Проверочный Byte построен на соединение всех предидущих Bytes через XOR
Я накарябал обработчик пакетов mousePack и qouckPack работают а вот streamPack работает через раз.
Если я отправляю файл размером 255 Byte то ардуина принемает его и обрабатывает без проблем. Если я шлю 314 Byte то исходя из максимального размера про пакет весь файл делится на два пакета (Первый размером 255) и (Последний размером 59) этот файл отрпавляется тоже без проблем. Но вот стоит мне отправить 315 Byte то паследний пакет не дахоит а Ардуина виснит.
Код обработчика у меня подучился ужастный, я даже больше чем уверен что проблема именно в нём, но разобраться сам не могу. Опыта не хватает.
Ещё есть подозрение на маленький буфер у ардуины, она просто не успивает обработать пакеты и переписывает буфер новыми данными. (Но это только предположение). Я юзаю Arduino UNO 328P
Прочитал тут что буфер максимально 64 Byte это что значит мне надо уварачивать пакеты с 255 до 64?
Вот такой вот ужастный у меня обработчик:
Прериписал функцию waitOfData c:
на
Ардуина перестала виснуть так как не ждёт бесконечно новых данных. Но пакеты также даходят не все. Менял максимум пакета до 64 Byte, стало больше пакетов дохадить но паследний также не доходит.
Как мне весь этот ужас привести в порядок и заставить его работать?
Вы бы ко всему вышенаписанному еще секцию объявления глобальных переменных выложили (в особенности хочется посмотреть на объявления массивов buffer и nutzDaten). Версия IDE тоже имеет значение (в используемой мною 23-й, например, буфер UART'а имеет длину 128 байт, в более поздних таки да - 64).
ЗЫ. О, нашел-таки спрятавшиеся объявления интересующих меня массивов - объявляются в теле функции onSerialEvent и имеют длину по 300 байтов. Жирненько - это во-первых. Во-вторых, по логике вещей, функция on SerialEvent запускается в ответ на аппаратное прерывание USART_RX, то есть каждый раз после того, как в приемном буфере UART'а появился новый байт. При этом в рассматриваемой функции создаются локальные переменные (в том числе и упомянутые массивы), обрабатываются все те несколько байтов, которые успевают упасть в UART-буфер за время выполнения функции, после чего функция завершает свою работу, высвобождая память, занятую под локальные переменные. То есть, фактически, вся принятая за текущий сеанс обработки события Serial информация должна теряться - при поступлении следующего байта вся обработка начнется с самого начала.
В таком случае должен сказать, что вам везет, если вы хоть что-то умудряетесь прочитать/обработать.
ЗЗЫ: Ожидание поступления очередного символа с помощью функции WaitOfData(), а потом обработка только одного поступившего байта - такой метод будет устойчиво работать только при скоростях обмена ниже скорости обработки. Если же за цикл ожидания/обработки будет поступать более одного байта, то риск переполнения входного буфера UART'а достаточно высок - а это приводит к неправильной обработке пакета.
Спасибки за ответ.
-- Я работаю с IDE Arduino 1.0.1 на Windows, но это только дла написания скетчей и их заливки. Пакеты я отправляю с помощью собственной програмы написанной на JAVA используя RXTX библиотеку. Пакеты отправляю полностью за раз, т.е. если у меня пакет размером 260 Byte то я отправляю с помощю Serial.write(весь пакет).
-- "300 байтов жирненько" вообще у меня пакеты больше 260 Byte не бывают, но в низ всегда можно урезать. Какое колличество было бы оптимальным? Может всётаки на 64 уменьшить?
-- "onSerialEvent" это не штатная ардуиновская функция, но вызывается именно с помощью serialEvent().
т.е. если я вас правельно понимаю то при каждом новом прерывании по USART_RX моя функция вызывается заново и "пререписывает" сама себя не обрабатывая до конца пакеты? Но в таком случае паследние пакеты должны доходить а первые терятся. У меня же на оборот. Если теряются пакеты то последнии.
-- Как "Где", "Когда" было бы правельно вызывать мою функцию instance()->onSerialEvent()? Можит написать свои прирывания по времени и постоянно проверять "нет ли чего прочитать" или же оставить как есть только дабавть флаг вроде FUNCTION_IS_READY который пре вызове instance()->onSerialEvent() будит садится на FALSE а при покидании функции на TRUE а потом:
Поможет ли мне это избежать "переписывания" функции? Ведь моя функция сама ждёт пока она не получит весь пакет. Не приведёт ли это к ещё большей потери данных? Ведь в таком случае я просто буду игнорировать вызовы оброботчика пока функция не закончит работу.
-- "такой метод будет устойчиво работать только при скоростях обмена ниже скорости обработки" какраз то и на оборот при работе на 9600 теряется больше пакетов чем на 115200 (проверенно методом тыка). На 115200 конторллер работает наиболее стабильно.
Появился новый вопросик. Всё так-же связанный с передачей пакетов. Я вроде дабился того что пакеты доходят.
Хотелось бы узнать мнение специалистов по поводу Контрольной суммы. Да я в курсе, интернэт пестрит различгными примерами CRC реализации. Но всёже вот мой вопрос:
Изночально я соединял весь пакет с помощью XOR, в итоге получал 1 Byte (uint8_t) для проверки. Потом порешил, что не мешало бы увеличить точность проверки и теперь я шлю 2 Byte (uint16_t) для контрольной суммы.
Вот так я считаю контрольную сумму:
Дак вот, изменилось ли что-нибудь после того как я сменил Datatype с uint8_t на uint16_t?
Я думаю что этого не достаточно и нужно как-то по другому считать сумму для 2 Байт.
Была идея Nr.: 1 просто складывать весь пакет с помощью +. При максимальной длинне пакета 256 в случае что пакет будит состоять только из 256, то я получаю максимальную сумму 65536. Что идеально подходит к uint16_t.
А потом предумал вот такой алгоритм Nr.: 2:
По советуйте пожалуйста какое из моих решений было бы оптимальней. И правельно ли я думаю по поводу простй смены типа данных.