Передача данных

faworit20
Offline
Зарегистрирован: 18.07.2017

Помогите разобраться с передачей пакета данных по серийнику. Вродебы все просто, а  не получается так как хочется. Вообщем идея такая, пакет состоит из символа и трех цыферек, типа (z456). Вот код:

void serialEvent()
{
  char n1=0, n2=0, n3=0;
  int n = Serial.available();
  int sk, k=0;
  for(int i=0; i < n; i++)               // считываем байт из приемного буфера в переменную
  {
    if(i==0)
    {
      k = Serial.read();
      Serial.println(k);
    }
    if(i==1)
    {
      n1=Serial.read();
    }
    if(i==2)
    {
      n2=Serial.read();
    }
    if(i==3)
    {
      n3=Serial.read();
    }
  }
  
  Serial.flush();
 
  //Serial.println(k);
  //Serial.println(n1);
  //Serial.println(n2);
  //Serial.println(n3);
  //Serial.println(sk);
  //if(Serial.available()==0)
  //{
    if(k==122)
    {
      Serial.println(n1);
      Serial.println(n2);
      Serial.println(n3);
      Serial.println("End");
    }
 
  //}
 
}
запускаю монитор ввожу пакет "z123" и... вижу обработка "z" условие выполняется, но куда делись n1,n2,n3?
И почему n1 отобразилось после закрытия условия иф?
122
 

 

End

49

 

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012
5N62V
Offline
Зарегистрирован: 25.02.2016

faworit20 пишет:

 

запускаю монитор ввожу пакет "z123" и... вижу обработка "z" условие выполняется, но куда делись n1,n2,n3?
 

Особо не вникал, но мне кажется, у Вас не правильно организован прием данных. Что-то пришло в сериал, фукция вызвалась, и сразу смотрит а сколько там в сериале байт? А там только один байт пока пришел, поэтому цикл фор в вашей функции выполнится только 1 раз. А остальное Вы очищаете флашем.

Попробуйте в начало функции вставить строчку

while(Serial.available() < 4); - это не сделает код правильным, но может быть заработает, и Вы разбиретесь что к чему.

 

faworit20
Offline
Зарегистрирован: 18.07.2017

Спасибо всем за советы, прошерстил кучу информации, но "воз и ныне там". В конце концов вытянул библиотечный скетч ReadASCIIString, собрал схемку управления светодиодом, загрузил в уно и нифига не получилось (афигеть!!!) нет регулировки яркости/цвета и нет ответа на мониторе. Пробовал несколько разных плат и все повторилось(платы рабочие). Как решить эту проблему с пакетами, подскажите, может я че пропускаю, но библотека должна ведь работать(я проверял не все скетчи и ввроде все работало). И последнее, программа ардуинка версия 1,8,0.

5N62V
Offline
Зарегистрирован: 25.02.2016

Хм. Эк Вы мечетесь-то. А можно глянуть на полный  код?

 

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

serialEvent не работает как вы ожидаете. Можете легко это проверить

void serialEvent()
{
  Serial.println(Serial.available());
}

Потом отправьте z123 и посмотрите что вам вернется ;)

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Хотите использовать serialEvent вот почти стандартный пример из ардуино иде

String inBuff = "";
bool buffComplete = false;

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if(buffComplete)
  {
    char k = inBuff[0];
    byte n1 = inBuff[1]-48;
    byte n2 = inBuff[2]-48;
    byte n3 = inBuff[3]-48;
    buffComplete = false;
    inBuff = "";
    Serial.println(k);
    Serial.println(n1);
    Serial.println(n2);
    Serial.println(n3);
  }
}

void serialEvent()
{
  while(Serial.available())
  {
    char inChar = (char)Serial.read();
    inBuff += inChar;
    if (inChar == '\n')
      buffComplete = true;
  }
}

Кому не нравится String можно заменить на массив. Принцип не изменится, только за указателем на элемент массива надо следить.

faworit20
Offline
Зарегистрирован: 18.07.2017

Добрый день. Ну посмотреть полный код... да в принципе можно, но ладно мой код не фурыкает, так ведь и библиотечный сериал(см. в предыдущем посте) также не регулирует светодиоды. А что касамо моего кода - это управление станком и связь с иполнительной платой (ардуинка уно) по серинику с ведущей платой. Одиночные команды идут без проблем, появилась необходимость немного переделать управление не одиночными коммандами а пакетом, типа код комманды + код операции + к.с. + подтвеждение. Извиняюсь за длинную писанину, станок пока работает и я пока долбаюсь с прогой(перевод на пакетный режим). Че я выяснил, массив не заполняется значениями в цикле, выводится только первая комманда а операция и т.д. все в нулях :(. Ну и последнее что хочу попробовать, это на другом компе установить идэ ардуинку и посмотрю как будет работать прошивка платы с библиотечной прогой серийника. Скачал еще новую версию 1,8,6. ардуины, может поможет. НА форуме почитал про серийник(его работу) и не видел подобной проблемы, это получается только у меня такая засада, вот досада :(

5N62V
Offline
Зарегистрирован: 25.02.2016

faworit20 пишет:

 Че я выяснил, массив не заполняется значениями в цикле, выводится только первая комманда а операция и т.д. все в нулях :(. Ну и последнее что хочу попробовать, это на другом компе установить идэ ардуинку и посмотрю как будет работать прошивка платы с библиотечной прогой серийника. Скачал еще новую версию 1,8,6. ардуины, может поможет. НА форуме почитал про серийник(его работу) и не видел подобной проблемы, это получается только у меня такая засада, вот досада :(

Именно про это я Вам и говорил. И даже рассказал почему.  Вы сделали то, что я советовал?

faworit20
Offline
Зарегистрирован: 18.07.2017

Да я проверил ваше предложение но это мало что мне дало. Собственно говоря разницы между цыклами я не увидел, не говоря уже об особенностях их применени. А Вот почему так происходит что-то я не видел вашего расказа, извините наверно слепой стал, если не трудно поясните еще раз

5N62V
Offline
Зарегистрирован: 25.02.2016

Так вроде пост мой никуда не удалился.     Кстати, а как у Вас вызывается функция void serialEvent()  ?

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

А мои посты видно? :)

faworit20
Offline
Зарегистрирован: 18.07.2017

Ребята, да в том-то и дело что и функции и текстовка прописывается (под мою задачу) в корне ничем не отличается от аналогичных и работающих. Я осмелюсь уточнить(ранее писал), залил на уно скетч из библиотеки ReadASCIIString и результат был нулевой!!! Проверил по длинне приема, пакет соответствует а вот ни регулировки светодиода ни ответа на монитор не получил. ПОучается что по иф условие не обрабатывается, т.е. код конца пакета ('\n') почемуто не нашелся(его естественно вставлял в строку). Вчера вечером повозился с ранее написанными скетчам(пакетной оправкой не занимался) и получается что проблема только с пакетами, неважно какой длинны(больше 1 байта). А байт ловит приемник без проблем. Но там нет цыклов реад, на важно каким цыклом читаем (for или while). Надо попробовать читать без них, щас проверю!

faworit20
Offline
Зарегистрирован: 18.07.2017

А ларчик просто открывался, переустановил ардуинку иде, все заработало. Новую 1,8,6, не ставил, просто переустановил(полностью) 1,8,0, и все! Незнаю почему мне так "повезло" что прога глюканула и довольно интересно, чуть было не полысел :(. Вообщем ребята всем спасибо за помощь.

5N62V кстати про функцию serialEvent()? Что значит как вызывается, я просто ввел это прерывание по наличию в приемном буфере чегото и все!  Функция работает пока буфер не опустеет, ну а дальше уж че надо то и буду делать с полученными данными. 

ну вот куски чего у меня получилось:

void loop() 
{
 
  if(buffComplete)
  {
    char k = inBuff[0];
    byte n1 = inBuff[1]-48;
    byte n2 = inBuff[2]-48;
    byte n3 = inBuff[3]-48;
    
    buffComplete = false;
    inBuff = "";
    int NumComm = (n1*100) + (n2*10) + n3;
    Serial.println(k);
    Serial.print(NumComm);
  }
 
Дальше в проге уже буду обрабатывать саму комманду(k) и данные(NumComm). И обработка пакета:
void serialEvent()
{
  while(Serial.available())
  {
    char inChar = (char)Serial.read();
    inBuff += inChar;
    if (inChar == 'n') buffComplete = true;
  }
}
И кстати, почему было в цыкле было if (inChar == '\n') buffComplete = true; ??? переменная inChar забирает из буфера по одному байту, и "\n" это два символа с разными индексами, или я не прав?

 

b707
Онлайн
Зарегистрирован: 26.05.2017

faworit20 пишет:

 

ну вот куски чего у меня получилось:

void loop() 
{
 
  if(buffComplete)
  {
    char k = inBuff[0];
    byte n1 = inBuff[1]-48;
    byte n2 = inBuff[2]-48;
    byte n3 = inBuff[3]-48;
    
    buffComplete = false;
    inBuff = "";
    int NumComm = (n1*100) + (n2*10) + n3;
    Serial.println(k);
    Serial.print(NumComm);
  }
 
Дальше в проге уже буду обрабатывать саму комманду(k) и данные(NumComm). И обработка пакета:
void serialEvent()
{
  while(Serial.available())
  {
    char inChar = (char)Serial.read();
    inBuff += inChar;
    if (inChar == 'n') buffComplete = true;
  }
}
И кстати, почему было в цыкле было if (inChar == '\n') buffComplete = true; ??? переменная inChar забирает из буфера по одному байту, и "\n" это два символа с разными индексами, или я не прав?

 

И что, этот код работает? - тогда ждем второй серии...

"почему было в цыкле было if (inChar == '\n')" - потому что '\n' - правильно, а 'n' - это чушь.

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

Penni пишет:

А мои посты видно? :)

заклятие невидимости сними с постов - будут видны.

5N62V
Offline
Зарегистрирован: 25.02.2016

Penni пишет:

А мои посты видно? :)

Видно! Классный код! Он работает несмотря на одну неприятную особенность функции serialEvent(). Чуть добавим в Ваш код переменных, чтоб увидеть как работает эта функция (3 и 4 строка - переменные, 26-29 строки - вывод их на печать.)

String inBuff = "";
bool buffComplete = false;
byte number = 0; //байт в буфере на момент вызова ф-ии
byte number1 = 0;// кол-во проходов цикла while

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if(buffComplete)
  {
    char k = inBuff[0];
    byte n1 = inBuff[1]-48;
    byte n2 = inBuff[2]-48;
    byte n3 = inBuff[3]-48;
    buffComplete = false;
    inBuff = "";
    Serial.println(k);
    Serial.println(n1);
    Serial.println(n2);
    Serial.println(n3);
  }
  if(number){
    Serial.print(" number = ");Serial.print(number);Serial.print("  number1 = ");Serial.println(number1);
    number = number1 = 0;
  }
}

void serialEvent()
{number = Serial.available();
  while(Serial.available())
  {
    char inChar = (char)Serial.read();
    //delay(1);
    inBuff += inChar;
    if (inChar == '\n')
      buffComplete = true; 
      number1++;
  }

}

Запускаем, вводим те же z123 (не забывая в настройках монитора выбрать "Новая строка") получаем в монитор порта:

 number = 1  number1 = 1
 number = 1  number1 = 1
 number = 1  number1 = 1
z
1
2
3
 number = 2  number1 = 2
 
Получается, что функция работает быстрее прихода очередного байта на этом бодрейте, и вызывается при каждом последующем байте , приходящем в сериал.  Чтоб убедиться в этом, раскамянчиваем //delay(1);  , и теперь результат будет таким:
 
z
1
2
3
 number = 1  number1 = 5
 
Вывод: эту особенность стоит учитывать. Быстро работающая функция - это классно, но не всегда классно отвлекать программу на обработку всего одного байта.  Если и идти на это, то обязательно НАКАПЛИВАТЬ в буфере, как в данном коде, а не записывать каждый раз в буфер по новой, как указывается даже в некоторых стандартных примерах.
Для себя я делаю задержку, чтоб ожидаемое кол-во байт успело прийти, но обязательно с контролем таймаута. 
 

Уважаемый ТС, как смог, объяснил.  У Вас та же проблема.

faworit20 пишет:
"\n" это два символа с разными индексами, или я не прав?

\n - это один символ с кодом 0x0A

b707
Онлайн
Зарегистрирован: 26.05.2017

5N62V пишет:

[

Запускаем, вводим те же z123 (не забывая в настройках монитора выбрать "Новая строка") получаем в монитор порта:

 number = 1  number1 = 1
 number = 1  number1 = 1
 number = 1  number1 = 1
z
1
2
3
 number = 2  number1 = 2
 
Получается, что функция работает быстрее прихода очередного байта на этом бодрейте, и вызывается при каждом последующем байте , приходящем в сериал. 

Это штатное поведение функции SerialEvent() - она вызывается на каждый приходящий символ, так и должно быть.

5N62V
Offline
Зарегистрирован: 25.02.2016

Я так и понял, просто эту особенность надо юзать осознанно.  Лично я пятьсот раз подумаю о возможных последствиях дерганья программы по прерыванию, особенно когда loop большой и медленный. В конце-концов данные и в сериале могут полежать, никуда не денутся, пока основной цикл не проверит их наличие. 

 

b707
Онлайн
Зарегистрирован: 26.05.2017

5N62V пишет:

Я так и понял, просто эту особенность надо юзать осознанно.  Лично я пятьсот раз подумаю о возможных последствиях дерганья программы по прерыванию, особенно когда loop большой и медленный. В конце-концов данные и в сериале могут полежать, никуда не денутся, пока основной цикл не проверит их наличие. 

похоже, вы все-таки не вполне понимаете функцию SerialEvent(). Прерывания в ней не используются, иначе введением delay() вы не смогли бы изменить ее поведение.

Функция SerialEvent() тупо вызывается один раз за loop(), ее использование абсолютно равнозначно добавлению в loop() кода

if (Serial.Availiable() ) { .... }

5N62V
Offline
Зарегистрирован: 25.02.2016

b707 пишет:

 

похоже, вы все-таки не вполне понимаете функцию SerialEvent(). Прерывания в ней не используются, иначе введением delay() вы не смогли бы изменить ее поведение.

Функция SerialEvent() тупо вызывается один раз за loop(), ее использование абсолютно равнозначно добавлению в loop() кода

if (Serial.Availiable() ) { .... }

Хм. Ок. Может и так. А в каком месте loop()   производится проверка if (Serial.Availiable() ) { .... }, и из чего это следует?

b707
Онлайн
Зарегистрирован: 26.05.2017

5N62V пишет:

Хм. Ок. Может и так. А в каком месте loop()   производится проверка if (Serial.Availiable() ) { .... }, и из чего это следует?

это следует из описания https://www.arduino.cc/en/Tutorial/SerialEvent - см вторую фразу. Проверка происходит _между_ вызовами loop().

И вот еще цитата оттуда же:

/*
  SerialEvent occurs whenever a new data comes in the hardware serial RX. This
  routine is run between each time loop() runs, so using delay inside loop can
  delay response. Multiple bytes of data may be available.
*/

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Если быть точнее, то вот так она вызывается:

int main(void)
{
	init();
	initVariant();
...
	setup();
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
        
	return 0;
}
...
void serialEventRun(void)
{
  if (Serial0_available && serialEvent && Serial0_available()) serialEvent();
...
}

Т.е. если у вас в loop-e будет что-то типа while(true) {if (Serial.available() ...) {...} }, то сериаливент вам не нужен в принципе. Да и вообще не особо нужен, по моему мнению. Разве что любителям хардкорного ООП-а она пригодится.

5N62V
Offline
Зарегистрирован: 25.02.2016

sadman41, b707   - Спасибо! Конструктивно!  И с какого будуна я взял, что эта ф-ция работает по прерыванию?! :)) Где-то читал, так в памяти отложилось. 

b707
Онлайн
Зарегистрирован: 26.05.2017

sadman41 пишет:

 сериаливент вам не нужен в принципе. Да и вообще не особо нужен, по моему мнению. Разве что любителям хардкорного ООП-а она пригодится.

ну как сказать, я не любитель ООП, а сериал ивент использую. Она уменьшает число операторов в loop() - а сл-но, улучшает читаемость кода. На мой взгляд, ясность программы в большинстве случаев важнее. чем выигрыш десятка микросекунд.

sadman41
Offline
Зарегистрирован: 19.10.2016

b707 пишет:

ну как сказать, я не любитель ООП, а сериал ивент использую. 

Так я вам и не запрещаю, для этого у нас Клапауций имеется.

Мне вот проще в лупе держать while, экономить микросекунды и на лету анализировать входящие данные без дополнительного складывания в буфер и повторного прохождения по нему.  Каждому своё.

5N62V
Offline
Зарегистрирован: 25.02.2016

а я вообще в нескольких проектах обращался из лупа к сериалу только тогда, когда в нем кол-во байт приближалось к нужному, в зависимости от кол-ва байт в пачке и скорости лупа

if (Serial.available()>x)function();

 

sadman41
Offline
Зарегистрирован: 19.10.2016

5N62V пишет:

if (Serial.available()>x)function();

Пока входящий в Serial пакет не более 16 байт - и это допустимо.