2

I am working on a project that has my computer communicating with an arduino board that reads the sensor output and put it on the serial port only if a "t" was received.the arduino code as shown below is working.

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);
    }
  }
}

My problem is in the C code when Im trying to read what arduino puts on the serial port. My C code is:

#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 is always=0 and i can't figure out what is the problem. Thanks in advance.

Youmna Habchy
  • 107
  • 2
  • 4
  • 17
  • Is the sleep long enough for a response? – suspectus Jul 16 '13 at 11:23
  • I already tried to change it to sleep(1000) but n still = 0 and I think it's enough because arduino has a quick response. – Youmna Habchy Jul 16 '13 at 11:44
  • At least use a program like Putty to verify that your PC can receive data from the serial port. Wiring mistakes are of course an explanation, not taking care of handshaking is another. Using O_NONBLOCK, the number 5 and not configuring the port settings like baudrate are also good ways to get yourself into trouble. – Hans Passant Jul 16 '13 at 11:45
  • I'm not sure if I understood what you mean but when I was reading from the serial port without writing then reading, it was working so my PC can receive data from the serial port. Writing without reading was working also, the problem is when I'm using them together. Thank you for helping me. – Youmna Habchy Jul 16 '13 at 11:52
  • Debug idea: get back to where read was working (without writing). Then change your `open()` to allow writing, but then perform no writes. The idea is to determine if it is your open() call or your write() call beings the problem. – chux - Reinstate Monica Jul 16 '13 at 13:00
  • Do you have the ability to put a loop-back connector on your serial port? (E. g. on a 9-pin D-sub, short pins 2 & 3.) – chux - Reinstate Monica Jul 16 '13 at 13:22
  • I tried what you suggested and after allowing writing in open() without writing, the read() keeps working normally – Youmna Habchy Jul 16 '13 at 13:24
  • Do you mean I should connect the pin RX to TX of the arduino board?? – Youmna Habchy Jul 16 '13 at 13:29

5 Answers5

3

i can't figure out what is the problem

One big problem is that the C program running on the "computer" is incomplete.

The Arduino's program does a serial port setup of at least the baud rate (and whatever else might be performed by default).
But the "computer's" C program never properly configures the serial port. The serial port will use whatever attributes (baud rate, data length, parity setting, canonical versus raw mode) previously configured, which will cause unpredictable reads and writes. (A loopback test would probably produce a false positive result.)

Use the POSIX Serial Port guide or this answer for sample code.

For canonical mode you probably need to add code like (assuming 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 */
    }

You probably should delete the line

fcntl(fd, F_SETFL, FNDELAY);  

as well as the O_NONBLOCK option in the open() call.

Community
  • 1
  • 1
sawdust
  • 16,103
  • 3
  • 40
  • 50
  • Thank you for your answer but honestly I didn't understand the code, there isn't another way to solve the problem without serial programming? Thanks in advance. – Youmna Habchy Jul 17 '13 at 08:40
  • There's the `stty` command, but that is not 100% reliable like coding the configuration in the program. You need to read the [POSIX Serial Port guide](http://www.cmrr.umn.edu/~strupp/serial.html) and/or [this answer](http://stackoverflow.com/questions/12437593/how-to-read-a-binary-data-over-serial-terminal-in-c-program/12457195#12457195) for sample code. – sawdust Jul 17 '13 at 09:56
  • After reading the POSIX Serial Port guide, I edit my code(as you suggested) and I still have n=0 and c=� , I'll post my new code in an answer. – Youmna Habchy Jul 17 '13 at 11:25
  • (1) You should post the revised code as an "edit" to your question, not as an answer. (2) Your revised code has the `tcsetattr()` in the wrong place to be effective, so of course there is no improvement. (3) Return codes from `tc[gs]etattr()` need to be tested. (4) You didn't remove the `fcntl()`, and added `O_NODELAY` which is probably wrong. (5) You made more edits than necessary, and changed the program from reading a whole line to reading just one char at a time. You need to choose canonical mode or raw mode, and make the attributes consistent with the `read()` logic. – sawdust Jul 17 '13 at 17:52
0

try this

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

instead of

int n=read(fd,tempbuf,5);
Aditya Ponkshe
  • 3,840
  • 4
  • 39
  • 58
0

Shouldn't you terminate the data sent with '\0'? Serge

Serge Bollaerts
  • 324
  • 2
  • 6
0

Reading the description of read() (shown below) tells us that n = 0 when you reach the end of file. Since the serial is not sending a \0, the read will continue until the end of file is reached. Therefore I think n==0 is the result you want.

So, maybe your if (n>0)

test should be if (n==0)

Can you see the characters you expect in your buffer using the debugger?

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

The read() function attempts to read nbytes from the file associated with handle, and places the characters read into buffer. If the file is opened using O_TEXT, it removes carriage returns and detects the end of the file.

The function returns the number of bytes read. On end-of-file, 0 is returned, on error it returns -1, setting errno to indicate the type of error that occurred.

Jeff
  • 1,364
  • 1
  • 8
  • 17
0

Thank you for your answers. This is my final code:

#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;
}
Youmna Habchy
  • 107
  • 2
  • 4
  • 17
  • The proper thanks would be to "accept" the answer that explains how you got to this final code. – sawdust Jul 18 '13 at 18:03