синхронизация с данными (Serial порт)
- Войдите на сайт для отправки комментариев
В процессе изучения программирования последовательного порта никак не могу добиться четкой синхронизации записи/чтения данных. Поясню: ардуина отправляет в порт фиксированный объем данных с определенной частотой. В реале это данные с акселерометра. Данные читаются и обрабатываются на компе в processing-е. Но вот в чем дело: квантование происходит неоднородно, разница зависит от частоты отправки в ардуине. Написал скетч для ардуины и для принимающей стороны на processing
Ардуино:
#include <TimerOne.h> boolean readyToSend; void setup() { Serial.begin(115200); pinMode(13, OUTPUT); Timer1.initialize(10000); // 100Hz Timer1.attachInterrupt(handler); } void loop() { if(readyToSend) { Serial.print("test message"); Serial.print('\n'); readyToSend = false; } } void handler() { readyToSend = true; }
Processing:
import processing.serial.*; Serial comPort; long timestamp; int count; void setup() { comPort = new Serial(this, "COM5", 115200); comPort.bufferUntil('\n'); delay(100); timestamp = millis(); } void draw() { if (millis() - timestamp >= 1000) { print(count); println(" Hz"); count = 0; timestamp = millis(); } } void serialEvent(Serial comPort) { count ++; }
В результате выполнения кода на процессинге должно выводиться значение частоты в Hz, которая задается в коде установки таймера на ардуино. По идее, значение долно быть строго постоянным, но на практике оно гуляет с определенной погрешностью (100 +-3 Гц) Причем у увеличением частоты таймера погрешность увеличивается тоже. Кто тут лажает? Ардуина? Код? Я?
Скорее всего - вы.
Причем не сколько в коде, сколько "концептуально". Сам Serial не является средой с гарантированным временем доставки. Так что "боротся можно", но получив удовлетворяющую стабильно это все равно будет это все равно будет "пока ветер не переменится".
Как минимум со стороны компа у вас - не realTime операционная система, следовательно гарантии что какие-то вещи выполняются в гарантированно предсказуемый срок - не будет никогда. А уж на языках типа процессинга (если я не ошибаюсь под капотам там java-живет) - подавно. Никогда нельзя узнать когда сброщик мусора запустится или самой операционке надумается с диском посвопить.
Далее как ни крути, а отправка в serial и сам процесс "рисования" - тоже занимает время, а в этот момент могут произойти тики - следовательно "тайминги сбиваются".
Можно попробовать:
Но еще раз. Все это "шаманство". Которое если и "поможет", то только "сдесь и сейчас".
Более "непробиваемым" решением, было-бы в каждом сообщении слать его временную метку (или просто сами временные метки считать сообщениями). Тогда приняв от ардуины "ее текущие время время" вы сможете определить у нее началась новая секуда или "еще старая идет". И какие-то "временные задержки канала" - перестанут играть роль.
P.S. и Serial.print("...");Serial.print('\n"); можно заменить на один Serial.println("...")
Вот СПАСИБО! Скажу честно - с некоторой ревностью глядел на развивающиеся по ответам соседние ветки и тоскливо обновлял страничку - а темка моя пустовала...И тут ичерпывающий ответ. Не понял что значит "более непробиваемое" решение - идея отсылать временные метки с ардуины уже в очереди на проработку, "непробиваемое" - имеется ввиду более верное или наоборот?
PS: про println в курсе, в оправдание могу сказать, что отличие есть, println отсылает 2 байта в конце, а для маркера достаточно одного ('\n') - экономия :)
Не понял что значит "более непробиваемое" решение - идея отсылать временные метки с ардуины уже в очереди на проработку, "непробиваемое" - имеется ввиду более верное или наоборот?
Верное или нет - не знаю. Все завист от поставленных целей. Это вам решать.
"непробиваемое" имелось ввиду решение которое не зависит от возможных задержек в канале передачи и того "что происходит с компом". Так как, в этом случае, фиксация времени события, происходит на стороне ардуины. Которая реалтаймова (следовательно стабильности таймингов добится легче, хотя тоже не всегда просто).
Возможно имеет смысл всю эту "частоту" (увеличения канутера) - тоже делать на стороне ардуины, а компу отсылать уже "частоту", а не "событие случилось" или "когда событие случилось". Тогда и со стороны компа логика упростится.
про println в курсе, в оправдание могу сказать, что отличие есть, println отсылает 2 байта в конце, а для маркера достаточно одного ('\n') - экономия :)
Вот за это мне и нравится помогать на форуме. Хочешь не хочешь - а всегда что-то новое узнаешь. Столько времени уже вожусь, а как-то проскользнули эти строки документации мимо сознания. Действительно там еще \r высылвается. И, судя по исходнику это поведение, - никак не настраивается.
ну тут уж вам решать, что важней "байт экономии" (смешно выглядит на фоне общей длины строки) или "читабильность кода".
Результаты попыток синхронизации в варианте регистрации временных интервалов на стороне ардуино. Исходный скетч:
Вычисляемые значения dt (по идее, вычисленные средствами ардуины) читаем в processing (впрочем, можно и просто в serial монитор заглянуть)
Для удобства чтения полученная строка выводится раз в секунду.
Что сказать...Тоже не все гладко и прозрачно. Вместо ожидаемых стабильных значений 10000 результат все же имеет небольшую погрешность (понятно, с учетом того, что значение micros() кратно 4-м:)
Возможно, я предъявляю слишком уж высокие требования к стабильности частоты, ибо для 10000 погрешность в +- 12 это ни о чем...Но если перевести вычисления в millis(), то результат такой же - имеем разброс от 10 до 12.
В чем причина? Какие существуют методы стабилизации частоты?
Читаем arduino.ru/Reference/Micros
У меня получилось так
Согласен, лучше. Правда, иногда еще едкие 10008 и 10012 проскакивают. Т.е все так и должно быть?
я когда лепил... то сделал проще, грубо говоря внешее прерывание, т.к. были часы на DS1307, брал секунды и сравнивал их, после изменения опрашывал датчик темперауры, но отправлял эти данные только после изменения времени в следующий раз, тебе можно сразу... собственно к чему я это, в какой-то из библиотек к этим же часам, была настройка скорости изменения данных, толи переход в микросекунды толи скорость обновления, нпомню натыкался на это с 4 месяца назад, т.е. мужно было сделать как бы прерывание по таемеру. Там же брать таймстамп. Вот как-то так, может чем помог)