Serial.read: почему разбивается строка приема?

vgrigorets
Offline
Зарегистрирован: 14.04.2015

Никак не при ходит в голову почему так. Отправляю в Serial строку, программа выдает два результата: первый байт как отдельную строку и остаток строки отдельно. А нужна строка целиком.

 

uint8_t bytes;
char buffer[255];

void setup() {
  Serial.begin (115200);
  delay(3000);
  Serial.println ("Ready!");
  buffer[0] = 0;
}

void loop() {
  bytes = Serial.available();
  if (bytes > 0) {
    Serial.println ();
    Serial.print (">>> Bytes: ");
    Serial.println (bytes);
    uint8_t i = 0;
    while (i < bytes) {
      buffer[i] = (char) Serial.read();
      Serial.print (">>> Copying ");
      Serial.print (i);
      Serial.print (" byte ");
      Serial.println (buffer[i]);
      i++;
      delay (10);
    }
    buffer[i] = 0;
    Serial.print (">>> Buffer: ");
    Serial.println (buffer);
    Serial.println();
  }
}

 

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

Простое совпадение.

Поставьте скорость меньше, уберите delay(), переставьте операторы местами - картина изменится. RX буфер объекта Serial заполняется по прерыванию. Поэтому он может быть дополнен на середине любой операции не блокирующей прерывания. 

Нужна строка целиком - используйте терминатор строки или ловите паузу (за секунду не увеличился буфер - значит передача окончена).

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

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

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Возможно пока вы что-то "обрабатываете" приходят ешё данные?
То есть вы не всю сторку считали.....
Попробуйте всавить после 23 строчки две вот таких:

Serial.println (bytes);
Serial.println (Serial.available());

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

vgrigorets пишет:

Отправляю в Serial строку

Надо для себя ответить на вопрос: что есть строка. Например, если вы отправляете "Hello, world!", а в буфер на момент проверки попало только "Hell" - это строка? Формально - да, т.к. строка - это набор символов. Понимаете, о чём я? Надо иметь признак - что вот, данные ПОЛНОСТЬЮ упали в приёмный буфер. Что будет этим признаком - выбирать вам, например, этим признаком может служить символ перевода строки '\n', или ещё чего.

vgrigorets
Offline
Зарегистрирован: 14.04.2015

Сделал так:

void loop() {
  bytes = Serial.available();
  if (bytes > 0) {
    Serial.println ();
    Serial.print (">>> Bytes: ");
    Serial.println (bytes);
    uint8_t i = 0;
    while (Serial.available()) {
      while (i < bytes) {
        buffer[i] = (char) Serial.read();
        Serial.print (">>> Copying ");
        Serial.print (i);
        Serial.print (" byte ");
        Serial.println (buffer[i]);
        i++;
        delay (10);
      }
      if (Serial.available()) {
        bytes += Serial.available(); 
      }
    }
    buffer[i] = 0;
    Serial.print (">>> Buffer: ");
    Serial.println (buffer);
    Serial.println();
  }
}

 

b707
Offline
Зарегистрирован: 26.05.2017

vgrigorets пишет:

Сделал так:

void loop() {
  bytes = Serial.available();
  if (bytes > 0) {
    Serial.println ();
    Serial.print (">>> Bytes: ");
    Serial.println (bytes);
    uint8_t i = 0;
    while (Serial.available()) {
      while (i < bytes) {
        buffer[i] = (char) Serial.read();
        Serial.print (">>> Copying ");
        Serial.print (i);
        Serial.print (" byte ");
        Serial.println (buffer[i]);
        i++;
        delay (10);
      }
      if (Serial.available()) {
        bytes += Serial.available(); 
      }
    }
    buffer[i] = 0;
    Serial.print (">>> Buffer: ");
    Serial.println (buffer);
    Serial.println();
  }
}

 

по-моему, никакой разницы с первоначальным - строка ровно точно так же может биться на части.

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

vgrigorets
Offline
Зарегистрирован: 14.04.2015

Работает однако. Serial.available() возвращает нужное количество байт, их количество и читаем.

b707
Offline
Зарегистрирован: 26.05.2017

vgrigorets пишет:

Работает однако. Serial.available() возвращает нужное количество байт, их количество и читаем.

Случайность. Идея все равно неверная. 100% потом будет глючить.

Serial.available() у вас возвращает то количество байт, которое уже лежит в буфере. Если строка к этому моменту еще не пришла целиком - получите только часть.

Единственный рабочий метод - тот. что описал DIYMan.

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

b707 пишет:

Единственный рабочий метод - тот. что описал DIYMan.

категорично !

я уже писал как делаю, шлю (допустим) 0х55,0хАА это типа синхронизации, далее байт с длиной пакета, далее данные.

этот метод не рабочий ?

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

xDriver пишет:

b707 пишет:

Единственный рабочий метод - тот. что описал DIYMan.

категорично !

я уже писал как делаю, шлю (допустим) 0х55,0хАА это типа синхронизации, далее байт с длиной пакета, далее данные.

этот метод не рабочий ?

Это ровно то же самое, что описал я: у тебя признак конца пакета - вычисляемый от "байт с длиной пакета". Но - признак конца пакета - ЕСТЬ. У ТС - нет. В твоём случае  код будет таким, навскидку:

#pragma pack(push,1)
typedef struct
{
	uint8_t stx1;
	uint8_t stx2;
	uint8_t dataLen;
} PacketHeader;
#pragma pack(pop)

PacketHeader packet;

void loop()
{
	if(Serial.available() >= sizeof(packet))
	{
		packet.stx1 = Serial.read();
		packet.stx2 = Serial.read();
		packet.dataLen = Serial.read();
		
		if(packet.stx1 == 0x55 && packet.stx2 == 0xAA)
		{
			uint8_t readed = 0;
			uint8_t* data = new uint8_t[packet.dataLen];
			while(readed < packet.dataLen)
			{
				if(!Serial.available())
					continue;
				
				data[readed++] = Serial.read();				
				
			}
			
			// тут работаем с данными
			
			
			delete[] data;
		}
	}
} 

Чисто для демонстрации, что и при таком подходе есть ОДНОЗНАЧНЫЙ признак конца пакета с данными. О чём я и писал.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

vgrigorets пишет:

Работает однако. Serial.available() возвращает нужное количество байт, их количество и читаем.


Это до чтения, а после чтения должен быть ноль.

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

прочитал еще раз, ключевые слова "Что будет этим признаком - выбирать вам,"

согласен ! :)

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

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