синхронизация с данными (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 высылвается. И, судя по исходнику это поведение, - никак не настраивается.
ну тут уж вам решать, что важней "байт экономии" (смешно выглядит на фоне общей длины строки) или "читабильность кода".
Результаты попыток синхронизации в варианте регистрации временных интервалов на стороне ардуино. Исходный скетч:
#define OUTPUT_FREQUENCY 100 // Hz uint32_t dt, timeStamp, timeLimit; boolean timeReady; void setup() { Serial.begin(115200); timeLimit = 1000000 / OUTPUT_FREQUENCY; delay(100); } void loop() { timeReady = (micros() - timeStamp) >= timeLimit; if(timeReady) { dt = micros() - timeStamp; timeStamp = micros(); Serial.println(dt); Serial.flush(); } }Вычисляемые значения dt (по идее, вычисленные средствами ардуины) читаем в processing (впрочем, можно и просто в serial монитор заглянуть)
import processing.serial.*; Serial comPort; long timestamp; int count; String inString; void setup() { comPort = new Serial(this, "COM5", 115200); comPort.bufferUntil('\n'); delay(100); timestamp = millis(); } void draw() { if (millis() - timestamp >= 1000) { timestamp = millis(); println(inString); count = 0; } } void serialEvent(Serial comPort) { while (comPort.available () > 0) inString = comPort.readStringUntil('\n'); }Для удобства чтения полученная строка выводится раз в секунду.
Что сказать...Тоже не все гладко и прозрачно. Вместо ожидаемых стабильных значений 10000 результат все же имеет небольшую погрешность (понятно, с учетом того, что значение micros() кратно 4-м:)
Возможно, я предъявляю слишком уж высокие требования к стабильности частоты, ибо для 10000 погрешность в +- 12 это ни о чем...Но если перевести вычисления в millis(), то результат такой же - имеем разброс от 10 до 12.
В чем причина? Какие существуют методы стабилизации частоты?
Читаем arduino.ru/Reference/Micros
У меня получилось так
#define OUTPUT_FREQUENCY 100 // Hz uint32_t dt, timeStamp, timeLimit; boolean timeReady; void setup() { Serial.begin(115200); timeLimit = 1000000 / OUTPUT_FREQUENCY - 4; delay(100); } void loop() { // timeReady = (micros() - timeStamp) >= timeLimit; if((micros() - timeStamp) >= timeLimit) { dt = micros() - timeStamp; timeStamp = micros(); Serial.println(dt, DEC); Serial.flush(); } }Согласен, лучше. Правда, иногда еще едкие 10008 и 10012 проскакивают. Т.е все так и должно быть?
я когда лепил... то сделал проще, грубо говоря внешее прерывание, т.к. были часы на DS1307, брал секунды и сравнивал их, после изменения опрашывал датчик темперауры, но отправлял эти данные только после изменения времени в следующий раз, тебе можно сразу... собственно к чему я это, в какой-то из библиотек к этим же часам, была настройка скорости изменения данных, толи переход в микросекунды толи скорость обновления, нпомню натыкался на это с 4 месяца назад, т.е. мужно было сделать как бы прерывание по таемеру. Там же брать таймстамп. Вот как-то так, может чем помог)