LINUX.ORG.RU

Функция read() (СИ) в openwrt

 


0

1

Здравствуйте.

Может кто сталкивался с таким явлением, а может я что-то не так делаю...

Есть программа на СИ, которая работает на роутере и читает даные от ардуиы (/dev/ttyUSB), делается так:

...
int open_port()  
 {  
   int fd;  
   fd = open(device, O_RDWR | O_NOCTTY); 
...

   options.c_cflag |= (CLOCAL | CREAD); 
   options.c_cflag &= ~PARENB;  
   options.c_cflag &= ~CSTOPB;  
   options.c_cflag &= ~CSIZE;  
   options.c_cflag |= CS8;  
   options.c_cc[VMIN] = 1;  
   options.c_cc[VTIME] = 1;  
   options.c_lflag = ICANON;  
   options.c_oflag = 0;  
   options.c_oflag &= ~OPOST; 
   tcflush(fd, TCIFLUSH);
   tcsetattr(fd, TCSANOW, &options);
... 

...
while(!VINTR) 
    {  
      int bytes = 0;
      memset(bRead, 0, sizeof(bRead));

      if((bytes = read(fd, bRead, BUFSIZE))==-1) // read()
        {
          printf("Error_Read_from_Arduino\n");
        }

      printf("Rec: %s", bRead);
...

Из ардуины строка отправляется вот так:

   Serial.print(1); 
   Serial.print(" "); 
   Serial.print(2); 
   Serial.print(" "); 
   Serial.print(3); 
   Serial.println(); 

То есть на конце - \r\n.

Если запускать прогу на большом компе, то строка принимается полностью, то есть «1 2 3\r\n», bytes говорит что прочитано 7 символов.

Если запускать прогу на роутере, то принимается строка - «1 2 3\r» (6 символов), а потом отдельно принимется символ «\n».

То есть получается, что как будто функция read работает по другому.

Скажите, почему так? Или я где-то ошибаюсь?

Или надо где-то указывать компилятору, что считать концом строки?

Для компа компилятор gcc 4.8, для роутера gcc 4.6.



Последнее исправление: stD (всего исправлений: 1)

libc разный. На компе у тебя скорее всего glibc, а на роутере uClibc. Собери минимальную систему с uClibc в чруте(или скачай готовую, гугл в помощь) и попробуй запустить своё ПО оттуда. Предварительно прокинув /dev внутрь чрута, естественно

Pinkbyte ★★★★★
()

я бы в явном виде указал «setlinebuf(fd);» после открытия ком-порта или после tcsetattr().

imho проблема в буфферизации ввода/вывода.

vel ★★★★★
()
Последнее исправление: vel (всего исправлений: 1)
Ответ на: комментарий от vel

Добавил setlinebuf(fd);

Ругается

warning: passing argument 1 of ‘setlinebuf’ makes pointer from integer without a cast [enabled by default] setlinebuf(fd); ^ In file included from ardunetstd.c:6:0: /usr/include/stdio.h:347:13: note: expected ‘struct FILE *’ but argument is of type ‘int’ extern void setlinebuf (FILE *__stream) __THROW;

stD
() автор топика
Ответ на: комментарий от stD

setlinebuf() работает только с дескрипторами типа FILE *, а у тебя напрямую работа с системными дескрипторами типа int, она не подойдет.
Для read() все равно какие символы принимать, будь то это '\r', '\n' или еще что-то. Потому нельзя полагаться на то, что прочитается сразу вся строка, и читать нужно в цикле (read() может прочитать вообще прочитать всю строку по одному символу возвращая 1 в количестве принятых байт) до тех пор, пока вызов read() не заблокируется (потому лучше чтение совмещать с select/poll) или не вернет -1.

whoami
()
Последнее исправление: whoami (всего исправлений: 1)

Никто и не гарантирует, что считается запрошенное количество данных. Ты сам должен определять конец блока данных

If a read() is interrupted by a signal after it has successfully read some data, it shall return the number of bytes read.

four_str_sam
()

Позравляю, ты неправильно сделал чтение строк из последовательного порта. :)

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

Последовательный порт - ещё та хреновина. :) Никто не гарантирует, что при чтении порта в буфере окажется строка целиком. Может ты read вызывал (или у него завершился таймаут в этот момент если O_NONBLOCK не используешь) когда только половина строки была передана. Сколько приняли, столько и получил.

Ещё есть такая хрень как options.c_iflag и флаги INLCR, IGNCR, ICRNL. Проверь, чистишь ли ты свою options перед использованием.

Stanson ★★★★★
()
Ответ на: комментарий от four_str_sam

Спасибо за помощь. Однако всё равно не понятно, почему в одном случае read чётко считывает строку «1 2 3\r\n», а другом слачае думает что строка заканчивается символом «\r» ?

Как всё-таки правильней пользоваться функцией read, посимвольно считывать?

stD
() автор топика
Ответ на: комментарий от stD

Я уже описал: считывать не полагаясь на ее поведение, она может возвращать любое количество символов при каждом из вызовов, это ни в одном из стандартов не оговорено. Потому тут нужно читать в буфер в цикле с условием выхода по ошибке или блокировке.

whoami
()
Ответ на: комментарий от stD

Всё равно ничего не понимаю, вот часть приёмного кода:

 while((bytes = read(fd, bRead, BUFSIZE)) > 0)
	{
	  bRead[bytes] = 0; 
          break;
	}

      if(bytes < 0)
	{
          error_log("Error_Read_from_Arduino");
	}

...
printf("Reciv_from_arduino: %s | Bytes - %d\n", bRead, bytes);
...

В терминал выдаёт вот такое:

Str_NOT_equally
Reciv_from_arduino: 185 0 0 22
 | Bytes - 11
UDP_send: 185 0 0 22


Str_NOT_equally
Reciv_from_arduino: 
 | Bytes - 1
UDP_send: 


Str_NOT_equally
Reciv_from_arduino: 186 0 0 22
 | Bytes - 11
UDP_send: 186 0 0 22


Str_NOT_equally
Reciv_from_arduino: 
 | Bytes - 1
UDP_send:

Не обращайте внимания на «Str_NOT_equally» и «UDP_send: 186 0 0 22».

То есть по логике получается, что когда

(bytes = read(fd, bRead, BUFSIZE))

будет равна нулю, тогда, когда прочитает всю строку «185 0 0 22\r\n», но на деле выходит, что она получает «185 0 0 22\r», а вследующем цикле получает «\n». Как так получается?

stD
() автор топика
Ответ на: комментарий от stD

Вот весь код:

 while(!VINTR) 
    {  
      int bytes = 0;
      memset(bRead, 0, sizeof(bRead));

      while((bytes = read(fd, bRead, BUFSIZE)) > 0)
	{
	  bRead[bytes] = 0; 
          break;
	}

      if(bytes < 0)
	{
          error_log("Error_Read_from_Arduino");
	}

     //////////////////// Сравнение строк //////////////////////
     if(strcmp(bRead, str_iz_file)==0)
       {
         printf("Str_equally\n"); 
       }

     else
       { 
         printf("Str_NOT_equally\n"); 
         FILE *f;
         f = fopen(file_ardu_patch, "w"); 
         if(f == NULL) 
          {
            error_log("Error-Write to file");
          }

         fprintf(f, "%s", bRead);
         fclose(f);
         memcpy(str_iz_file, bRead, sizeof(str_iz_file)); 
       }
    
     printf("Reciv_from_arduino: %s | Bytes - %d\n", bRead, bytes);

     if(sendto(sockfd, str_iz_file, strlen(str_iz_file), 0, (struct sockaddr *) &server, sizeof(struct sockaddr_in)) < 0)
      {
        error_log("ERROR_udp_send");
      }

     printf("UDP_send: %s\n\n", str_iz_file);
     
    } // END (while) 
stD
() автор топика
Ответ на: комментарий от stD

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

whoami
()
Ответ на: комментарий от stD

ну это не совсем то, что нужно.

читаем строку до '\n' с отбросом того, что не влезло в буфер и игнорируем прерывание сискола.

Если нужно с таймаутом, то перед этим делаем alarm(X)

Примерно так:

    bzero(buf,sizeof(buf));
    i = 0;
    eol = 0;
    do {
        if(i < sizeof(buf)-1) {
                r = read(nc,&buf[i],sizeof(buf)-i-1);
                if(r > 0) {
                     i += r;
                     eol = strchr(buf,'\n') != 0;
                }
        } else {
                bzero(buf2,sizeof(buf2));
                r = read(nc,buf2,sizeof(buf2)-1);
                eol = strchr(buf2,'\n') != 0;
        }
    } while( !eol && ( (r > 0) || (r < 0 && errno == EINTR) ) );

Может кто-нибудь из модераторов перенесет это в development ?

vel ★★★★★
()
Ответ на: комментарий от vel

Здравствуйте ещё раз. Код Ваш я попробовал, результат тот же.

Большой комп:

Str_NOT_equally
Reciv_from_arduino: 272 0 0 22
 | Bytes - 12
UDP_send: 272 0 0 22


Str_NOT_equally
Reciv_from_arduino: 273 0 0 22
 | Bytes - 12
UDP_send: 273 0 0 22


Str_NOT_equally
Reciv_from_arduino: 274 0 0 22
 | Bytes - 12
UDP_send: 274 0 0 22


Str_NOT_equally
Reciv_from_arduino: 275 0 0 22
 | Bytes - 12
UDP_send: 275 0 0 22


Str_NOT_equally
Reciv_from_arduino: 276 0 0 22
 | Bytes - 12
UDP_send: 276 0 0 22

Всё окей, принимаются 12 байт.

Роутер:

Str_NOT_equally
Reciv_from_arduino: 561 0 0 22
 | Bytes - 11
UDP_send: 561 0 0 22


Str_NOT_equally
Reciv_from_arduino: 
 | Bytes - 1
UDP_send: 


Str_NOT_equally
Reciv_from_arduino: 562 0 0 22
 | Bytes - 11
UDP_send: 562 0 0 22


Str_NOT_equally
Reciv_from_arduino: 
 | Bytes - 1
UDP_send:

Принимаются 11 байт, а на следующей итерации один байт.

Как мне кажется, в случае с компом read() блокируется после приёма «\N», то есть приняв всю строку «276 0 0 22\r\n».

В случае с роутером read() блокируется после приёма «\R», то есть сначала принимает «562 0 0 22\r», а на следующей итерации «\N». Такое может быть?

stD
() автор топика
Ответ на: комментарий от stD

Победа!

Установил options.c_iflag = IGNCR;, теперь символ «\R» игнорируется и всё хорошо...

Str_NOT_equally
Reciv_from_arduino: 5514 0 0 22
 | Bytes - 12
UDP_send: 5514 0 0 22


Str_NOT_equally
Reciv_from_arduino: 5515 0 0 22
 | Bytes - 12
UDP_send: 5515 0 0 22


Str_NOT_equally
Reciv_from_arduino: 5516 0 0 22
 | Bytes - 12
UDP_send: 5516 0 0 22


Str_NOT_equally
Reciv_from_arduino: 5517 0 0 22
 | Bytes - 12
UDP_send: 5517 0 0 22

Спасибо всем откликнувшимся в целом и в частности Stanson.

stD
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.