Четене от сериен порт след запис върху него

Работя върху проект, при който компютърът ми комуникира с платка Arduino, която чете изхода на сензора и го поставя на серийния порт само ако е получено "t". Кодът на Arduino, както е показано по-долу, работи.

const int inputPin = 0;
void setup(){
  Serial.begin(9600);
  pinMode(13, OUTPUT);}

void loop(){
 if (Serial.available() > 0){
    char c=Serial.read();
   if(c=='t'){
      int value = analogRead(inputPin);
      float celsius = (5.0 * value * 100.0)/1024.0; 
      Serial.println(celsius);
    }
  }
}

Проблемът ми е в C кода, когато се опитвам да прочета какво поставя arduino на серийния порт. Моят C код е:

#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>

int main(){     
    int STATE_OK=0;
    int STATE_WARNING=1;
    int STATE_CRITICAL=2; 
    char tempbuf[10];
    int fd=open("/dev/ttyACM0",O_RDWR | O_NOCTTY | O_NONBLOCK);
    if(fd == -1){
            printf("Unable to open /dev/ttyACM0\n");
            return STATE_WARNING;
    } else {
        fcntl(fd, F_SETFL, FNDELAY);
        int w=write(fd, "t", 1);
        printf("The number of bytes written to the serial port is %d \n",w);
        fprintf(stderr, "fd = %d.\n", fd);
        sleep(10);
        int n=read(fd,tempbuf,5);
        printf("%d,%s \n",n,strerror(errno));
        if(n>0){
            float temp=atof(tempbuf);
            printf("Temperature is: %f Celsius\n", temp);
            if (temp>27){
                return STATE_CRITICAL;
            }else{
                printf("The temperature is %f Celsius and checked 10 seconds ago\n",temp);
                return STATE_OK;
            }
        }
    }
    close(fd);
    return 0;
}

n винаги е = 0 и не мога да разбера какъв е проблемът. Благодаря предварително.


person Youmna Habchy    schedule 16.07.2013    source източник
comment
Сънят достатъчно дълъг ли е за отговор?   -  person suspectus    schedule 16.07.2013
comment
Вече се опитах да го променя на sleep(1000), но n все още = 0 и мисля, че е достатъчно, защото arduino има бърз отговор.   -  person Youmna Habchy    schedule 16.07.2013
comment
Поне използвайте програма като Putty, за да проверите дали компютърът ви може да получава данни от серийния порт. Грешките в окабеляването, разбира се, са обяснение, друго е неспазването на ръкостискането. Използването на O_NONBLOCK, числото 5 и липсата на конфигуриране на настройките на порта като скорост на предаване също са добри начини да си навлечете проблеми.   -  person Hans Passant    schedule 16.07.2013
comment
Не съм сигурен дали разбрах какво имате предвид, но когато четях от серийния порт, без да пиша и след това да чета, той работеше, така че моят компютър може да получава данни от серийния порт. Писането без четене също работи, проблемът е, когато ги използвам заедно. Благодаря, че ми помогна.   -  person Youmna Habchy    schedule 16.07.2013
comment
Идея за отстраняване на грешки: върнете се там, където четенето работеше (без писане). След това променете своя open(), за да разрешите запис, но след това не извършвайте записвания. Идеята е да се определи дали проблемът е вашето open() повикване или вашето write() извикване.   -  person chux - Reinstate Monica    schedule 16.07.2013
comment
Имате ли възможност да поставите конектор за обратна връзка на вашия сериен порт? (Напр. на 9-пинов D-sub, къси щифтове 2 и 3.)   -  person chux - Reinstate Monica    schedule 16.07.2013
comment
Опитах това, което предложихте, и след като разреших писане в open() без писане, read() продължава да работи нормално   -  person Youmna Habchy    schedule 16.07.2013
comment
Искате да кажете, че трябва да свържа щифта RX към TX на платката arduino??   -  person Youmna Habchy    schedule 16.07.2013


Отговори (5)


не мога да разбера какъв е проблема

Един голям проблем е, че C програмата, работеща на "компютъра", е непълна.

Програмата на Arduino прави настройка на сериен порт на поне скоростта на предаване (и каквото и да е друго, което може да се извърши по подразбиране).
Но C програмата на "компютъра" никога не конфигурира правилно серийния порт. Серийният порт ще използва всички предварително конфигурирани атрибути (скорост на предаване, дължина на данните, настройка за паритет, каноничен срещу необработен режим), което ще доведе до непредвидими четения и записи. (Обратният тест вероятно би дал фалшив положителен резултат.)

Използвайте Ръководство за POSIX сериен порт или този отговор за примерен код.

За каноничен режим вероятно трябва да добавите код като (приемайки 8N1):

    rc = tcgetattr(fd, &tty);
    if (rc < 0) {
        /* handle error */
    }
    savetty = tty;    /* preserve original settings for restoration */

    spd = B9600;
    cfsetospeed(&tty, (speed_t)spd);
    cfsetispeed(&tty, (speed_t)spd);

    tty.c_cflag &= ~PARENB
    tty.c_cflag &= ~CSTOPB
    tty.c_cflag &= ~CSIZE;
    tty.c_cflag |= CS8;

    tty.c_cflag &= ~CRTSCTS;    /* no HW flow control? */
    tty.c_cflag |= CLOCAL | CREAD;

    tty.c_iflag |= IGNPAR | IGNCR;
    tty.c_iflag &= ~(IXON | IXOFF | IXANY);
    tty.c_lflag |= ICANON;
    tty.c_oflag &= ~OPOST;

    rc = tcsetattr(fd, TCSANOW, &tty);
    if (rc < 0) {
        /* handle error */
    }

Вероятно трябва да изтриете реда

fcntl(fd, F_SETFL, FNDELAY);  

както и опцията O_NONBLOCK в повикването open().

person sawdust    schedule 17.07.2013
comment
Благодаря ви за отговора, но честно казано не разбрах кода, няма ли друг начин за решаване на проблема без серийно програмиране? Благодаря предварително. - person Youmna Habchy; 17.07.2013
comment
Има команда stty, но тя не е 100% надеждна като кодирането на конфигурацията в програмата. Трябва да прочетете Ръководство за POSIX сериен порт и/или този отговор за примерен код. - person sawdust; 17.07.2013
comment
След като прочетох ръководството за сериен порт на POSIX, редактирах кода си (както предложихте) и все още имам n=0 и c=�, ще публикувам новия си код в отговор. - person Youmna Habchy; 17.07.2013
comment
(1) Трябва да публикувате преработения код като редакция на вашия въпрос, а не като отговор. (2) Вашият преработен код съдържа tcsetattr() на грешното място, за да бъде ефективен, така че, разбира се, няма подобрение. (3) Кодовете за връщане от tc[gs]etattr() трябва да бъдат тествани. (4) Не сте премахнали fcntl() и сте добавили O_NODELAY, което вероятно е грешно. (5) Направихте повече редакции от необходимото и променихте програмата от четене на цял ред на четене само на един знак наведнъж. Трябва да изберете каноничен режим или необработен режим и да направите атрибутите съвместими с логиката read(). - person sawdust; 17.07.2013

Опитайте тази

int n=read(fd,&tempbuf,sizeof(tempbuf));

вместо

int n=read(fd,tempbuf,5);
person Aditya Ponkshe    schedule 16.07.2013
comment
премахнете O_NONBLOCK и вижте какво ще се случи. - person Aditya Ponkshe; 16.07.2013
comment
Все още имам същия резултат. - person Youmna Habchy; 16.07.2013
comment
така че в printf(%d,%s \n,n,strerror(errno)); стойността на n винаги е нула? - person Aditya Ponkshe; 16.07.2013
comment
Да, резултатът е: 0, успех - person Youmna Habchy; 16.07.2013
comment
опитайте това fcntl(fd, F_SETFL, 0); - person Aditya Ponkshe; 16.07.2013
comment
тогава ми свършиха предложенията, съжалявам. - person Aditya Ponkshe; 16.07.2013

Не трябва ли да прекратите данните, изпратени с '\0'? Серж

person Serge Bollaerts    schedule 16.07.2013

Четенето на описанието на read() (показано по-долу) ни казва, че n = 0, когато стигнете до края на файла. Тъй като серийният номер не изпраща \0, четенето ще продължи до достигане на края на файла. Затова смятам, че n==0 е резултатът, който искате.

И така, може би вашето ако (n>0)

тестът трябва да бъде ако (n==0)

Можете ли да видите символите, които очаквате във вашия буфер, като използвате програмата за отстраняване на грешки?

read() #include int read( int handle, void *buffer, int nbyte);

Функцията read() се опитва да прочете nbytes от файла, свързан с манипулатора, и поставя прочетените символи в буфера. Ако файлът се отвори с помощта на O_TEXT, той премахва връщането на каретката и открива края на файла.

Функцията връща броя на прочетените байтове. При край на файла се връща 0, при грешка връща -1, задавайки errno да указва вида на възникналата грешка.

person Jeff    schedule 16.07.2013
comment
Благодаря, но винаги имам същия резултат. - person Youmna Habchy; 17.07.2013

Благодаря ви за отговорите. Това е окончателният ми код:

#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>
#include<unistd.h>
#include<fcntl.h>
#include<termios.h>

int main() {    

    int STATE_OK=0;
    int STATE_WARNING=1;
    int STATE_CRITICAL=2; 
    char tempbuf[10];
    struct termios tty;

    int fd=open("/dev/ttyACM1",O_RDWR | O_NOCTTY);
    if(fd == -1){
            printf("Unable to open /dev/ttyACM1\n");
            return STATE_WARNING;
    }else {
        if(tcgetattr(fd, &tty)!=0){
            perror("tcgetatt() error");
        }else{
                cfsetospeed(&tty, B9600);
                cfsetispeed(&tty, B9600);

                tty.c_cflag &= ~PARENB;
                tty.c_cflag &= ~CSTOPB;
                tty.c_cflag &= ~CSIZE;
                tty.c_cflag |= CS8;
                tty.c_cflag &= ~CRTSCTS; 
                tty.c_cflag |= CLOCAL | CREAD;

                tty.c_iflag |= IGNPAR | IGNCR;
                tty.c_iflag &= ~(IXON | IXOFF | IXANY);
                tty.c_lflag |= ICANON;
                tty.c_oflag &= ~OPOST;
                tcsetattr(fd, TCSANOW, &tty);

                int w=write(fd, "t", 1);/*printf("%d\n",w);
                fprintf(stderr, "fd = %d.\n", fd);*/
                usleep(1000);
                int n=read(fd,tempbuf,8);/*printf("%d \n",n);*/
                tempbuf[9]=0;
                float temp=atof(tempbuf);

                if (temp>27){
                    printf("CRITICAL: %f celsius\n",temp);
                    return STATE_CRITICAL;
                }else{
                    printf("Everything is OK and the temperature is %f Celsius\n",temp);
                    return STATE_OK;
                }
        }
    }
    close(fd);
    return 0;
}
person Youmna Habchy    schedule 18.07.2013
comment
Правилната благодарност би била да приемете отговора, който обяснява как сте стигнали до този окончателен код. - person sawdust; 18.07.2013