Read Write serial port

C

collinm

hi

we use linux and c language


under bash i do
echo -e \\000\\000\\000\\000\000\\001Z00\\002AA LINUX \\004 >/dev/ttyS2

that send command to a led display (alpha sign communication)

i need to do convert this code to a c programm under linux

i tried this code:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <stdio.h>

/* File descriptors for the ports */
int fd1, fd2;
char *buff,*buffer,*bufptr;

/* Opening port one for read and write */
int open_port1(void)
{
fd1 = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd1 == -1)
{
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
{
fcntl(fd1, F_SETFL, 0);
printf(" Port 1 has been sucessfully opened and %d is the file
descriptor\n",fd1);
}
return (fd1);
}

int open_port2(void)
{
fd2 = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd2 == -1)
{
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
{
fcntl(fd2, F_SETFL,FNDELAY);
printf(" Port 2 has been sucessfully opened and %d is the file
descriptor\n",fd2);
}
return (fd2);
}

int main()
{
int wr,rd;
open_port1();
open_port2();

char msg[]="\\000\\000\\000\\000\\000\\001Z00\\002AA LINUX \\004";

wr=write(fd1,msg,100);

printf(" Bytes sent are %d \n",wr);
if (wr < 0)
fputs("write() of 4 bytes failed!\n", stderr);
else
printf(" Stuff has been written into port 1\n");
sleep(1);

fcntl(fd2, F_SETFL,FNDELAY);
rd=read(fd2,buff,100);
printf(" Bytes recieved are %d \n",rd);
printf("%s",buff);

close(fd1);
close(fd2);
return 1;
}



but nothing happen on the led display

any idea?
 
U

Ulrich Eckhardt

collinm said:
we use linux and c language

echo -e \\000\\000\\000\\000\000\\001Z00\\002AA LINUX \\004 >/dev/ttyS2

that send command to a led display (alpha sign communication)

i need to do convert this code to a c programm under linux
int fd1, fd2;
int open_port1(void)
{
fd1 = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd1 == -1)
{
perror("open_port: Unable to open /dev/ttyS0 - ");
}
else
{
fcntl(fd1, F_SETFL, 0);
printf(" Port 1 has been sucessfully opened and %d is the file
descriptor\n",fd1);
}
return (fd1);
}

Bad idea: returning a value and storing it in a global at the same time.
Later on ignoring the returnvalue and using the global. Time for
refactoring.
char msg[]="\\000\\000\\000\\000\\000\\001Z00\\002AA LINUX \\004";

wr=write(fd1,msg,100);

Your mistake is that you don't know what gets sent through the serial port.
Seriously, the 'echo -e ..' is something you type at a sh prompt (I assume
you use sh or some similar shell). This line is subject to having certain
characters (in particular the backslash) interpreted specially before
invoking 'echo'. 'echo' in turn interprets another set of characters
specially before sending anything to its stdout. Now, the same scheme
applies to C sourcecode, which interprets the content of string literals
to generate binary strings.

IOW, you have to read manuals to first find out what gets sent to the
serial port and can then start redoing the same under C.

Uli
 
W

Walter Roberson

:we use linux and c language

;under bash i do
;echo -e \\000\\000\\000\\000\000\\001Z00\\002AA LINUX \\004 >/dev/ttyS2

:that send command to a led display (alpha sign communication)

:i need to do convert this code to a c programm under linux

:i tried this code:

:/* Opening port one for read and write */
:int open_port1(void)

Your code for open_port1() is the same as your code for
open_port2() except for the message about which port was opened.
In particular, it is bug-for-bug identical in claiming to open S0 but
really opening S2.

: fd1 = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NDELAY);

Why are you setting a global variable with the fd number, and
then ignorning the fd value returned by the routine? Don't mix
metaphors.

: char msg[]="\\000\\000\\000\\000\\000\\001Z00\\002AA LINUX \\004";

That doesn't correspond to what you are doing in bash. The correspondance
would be

char msg[] = {'\0', '\0', '\0', '\0', '0', '0', '\1', '0', '0',
'\2', 'A', 'A', ' ', 'L', 'I', 'N', 'U', 'X', '\4' };

a) in your bash version you have a \000 in one place instead of a \\000
b) in your bash version the characters after the Z are literal, not
numeric;
c) using char[] with an initialized string is going to result in a trailing
'\0' being put on that is not present in your original. As you are already
dealing with null characters, this could be significant.

: wr=write(fd1,msg,100);

Why are you writing 100 characters out of a string that is only 19
characters long? You should write(fd1, msg, sizeof(msg))

: printf(" Bytes sent are %d \n",wr);
: if (wr < 0)
: fputs("write() of 4 bytes failed!\n", stderr);

But it isn't 4 bytes you would have written!

: fcntl(fd2, F_SETFL,FNDELAY);
: rd=read(fd2,buff,100);
: printf(" Bytes recieved are %d \n",rd);

If you are reading from the same file as you are writting to, and
both fd's are RW, why not just use the same descriptor? Just make
sure you switch properly into reading mode by doing the appropriate
lseek().

: printf("%s",buff);

You are sending binary characters to the device, so why should we
expect that we will get null-terminated printable text back?
My expectation would be that there might be a null in the input,
and that would cause the %s format to stop printing.

Also, you said that the C program was to be the equivilent of the
bash, but in the bash you never read from the device.

One has to wonder whether the device is properly initialized to the
correct speed, parity, and so on, since there is no 'stty' shown,
nor tcsetattr() nor termio call.
 
C

collinm

Walter said:
: char msg[]="\\000\\000\\000\\000\\000\\001Z00\\002AA LINUX \\004";

That doesn't correspond to what you are doing in bash. The correspondance
would be

char msg[] = {'\0', '\0', '\0', '\0', '0', '0', '\1', '0', '0',
'\2', 'A', 'A', ' ', 'L', 'I', 'N', 'U', 'X', '\4' };

a) in your bash version you have a \000 in one place instead of a
\\000

why some characters have \ in front of them and some not?

where is the Z?

they surely miss a '\0', because in the msg array, there a 5 null

b) in your bash version the characters after the Z are literal, not
numeric;
ya i know, in bash i can mix octal characters with literal...

i need to do the same thing with the C program

c) using char[] with an initialized string is going to result in a trailing
'\0' being put on that is not present in your original. As you are already
dealing with null characters, this could be significant.

what i need to do to resolve that?

char *msg?
 
J

Jens.Toerring

collinm said:
Walter said:
: char msg[]="\\000\\000\\000\\000\\000\\001Z00\\002AA LINUX \\004";

That doesn't correspond to what you are doing in bash. The correspondance
would be

char msg[] = {'\0', '\0', '\0', '\0', '0', '0', '\1', '0', '0',
'\2', 'A', 'A', ' ', 'L', 'I', 'N', 'U', 'X', '\4' };

a) in your bash version you have a \000 in one place instead of a \\000

why some characters have \ in front of them and some not?

Because '\0' and '0' are two different characters. The first one ('\0')
is the character with the numerical value 0, while the second is the
character that will be printed as the character 0 but will have the
numerical value 48 if your machines uses the ASCII character encoding.
Same for the difference between '\1' and '1' etc. Or do you mean the
difference in bash? That's a question for comp.unix.shell or maybe
comp.unix.programmer since it's nothing related to C.
where is the Z?

Walter forgot it, just put it in where it's missing (i.e. after the
'\1' character).
they surely miss a '\0', because in the msg array, there a 5 null

Then put it in. The corrected version would be

char msg[ ] = { '\0', '\0', '\0', '\0', '\0', '\1', 'Z', '0', '0',
'\2', 'A', 'A', ' ', 'L', 'I', 'N', 'U', 'X', '\4' };
ya i know, in bash i can mix octal characters with literal...
i need to do the same thing with the C program

What is "the same thing"? Octal, decimal, hexadecimal or whatever
way a value is represented in the program is irrelevant.
c) using char[] with an initialized string is going to result in a trailing
'\0' being put on that is not present in your original. As you are already
dealing with null characters, this could be significant.
what i need to do to resolve that?

Take care that you don't use functions that expect strings (i.e. char
arrays that are terminated by a '\0' character) with that array and
to pass the correct length of the array to functions that operate on
simple arrays. You can determine the length by using e.g. 'sizeof msg'.

Regards, Jens
 
W

Walter Roberson

:Walter Roberson wrote:
:> char msg[] = {'\0', '\0', '\0', '\0', '0', '0', '\1', '0', '0',
:> '\2', 'A', 'A', ' ', 'L', 'I', 'N', 'U', 'X', '\4'
:};

:> a) in your bash version you have a \000 in one place instead of a
:\\000

:where is the Z?

As Jens indicated in his follow-up, I forgot that one when I was
typing it.


:they surely miss a '\0', because in the msg array, there a 5 null

Yes, in the msg array, but that original bash we were given as the
original value had \\000\\000\\000\\000\000 at the beginning. The
\\ introduces an octal character in that bash context, but notice that
before the fifth group there is only a single \ instead of a double \\ .
The single \ and the following 0 will be consumed by the shell at a
level before octal conversion is attempted, so in the bash version
there were four nulls followed by a silently-eaten \0 pair followed
by two literal characters 0 as strings.

:ya i know, in bash i can mix octal characters with literal...

:i need to do the same thing with the C program

You can generally do so in a double-quoted initializer, but there
are a couple of restrictions.

a) The double-quoted initializer wants
to tack a null on the end of the string, and it is not clear from
what you wrote before that that is acceptable; if not, then you cannot
use a double-quoted string (not unless you use it as a convenient
initializer but take a copy of it and strip off the trailing null
before actual use.)

b) A sequence starting with \ extends as far as possible, so
the sequence \00000 wants to consume the five 0's, whereas the
bash you presented wants to consume 3 0's and then have two
literal 0's. There are two ways around this:

1) Keep using \ escapes as far as necessary to reach something
that is not part of any possible escape sequence. For example,

"\0\x30\x30Z"

ends the first null byte because it sees the second one start;
then the \0x30 is one of the representations of the character 0
(as opposed to the -number- 0). You need another escape
after that to end the \x30 because the next character, 0, would
otherwise be eaten as part of the first \x30 too. But then
after that, the Z cannot possibly be part of any octal or
hex or decimal escape sequence, so it is "self-delimiting",
not needing anything to indicate that the previous escape
sequence has ended.

2) Sometimes it's easiest to break the double-quoted string
into two parts, and rely upon the fact that standard C will merge
adjacent double-quoted strings. For example,

"\0\0\0\0" "00Z"

The first four escapes are processed, but the first closing quote
ends the scope of the active escape sequence, so when the second
opening quote starts one is not in escape mode anymore and it is
the literal characters 0 0 Z that would be there.

:> c) using char[] with an initialized string is going to result in a
:trailing
:> '\0' being put on that is not present in your original. As you are
:already
:> dealing with null characters, this could be significant.

:what i need to do to resolve that?

If you need to work with "strings" with embedded nulls, you end up
needing to pass the size all over the place, one way or another
(because otherwise the code won't know where to leave off.) You can
initialize a char [] with a "" string and then pass around the length
-excluding- the trailing null you know to be there. Or you can do as I
demonstrated earlier, in which you use an aggregate initializer of byte
by byte. Or you can bother to create a new string that has the null
stripped out, except you don't gain much by doing so since you
still need to pass the sizes around.
 
W

Walter Roberson

about hex code i have with bash
CMD_INIT="\x00\x00\x00\x00\x00\x01Z00\x02"
CMD_END="\x04"
echo -e "${CMD_INIT}E$AAU0030FFFF${CMD_END}" >/dev/ttyS2
in C, i wrote
char msg1[]={"\x00\x00\x00\x00\x00\x01Z00\x02E$AAU0030FFFF\x04"};
that don't seem to be ok
with your information that could maybe transform to
char msg1[]={"\x00\x00\x00\x00\x00\x01\Z00\x02\E$AAU0030FFFF\x04"};

Close; you recognized correctly that the problem was the run-on
of the x02 towards the E, but \E is not the way to correct it.

The simplest correction would likely be:

char msg1[]={"\x00\x00\x00\x00\x00\x01\Z00\02E$AAU0030FFFF\x04"};

Notice the lack of the 'x' before the 02. Without the x it's
an octal escape instead of a hex escape. E is not a valid octal
character so the parser would end the escape at the 2 and treat the
E as a plain text character.
 
D

Dave Thompson

:i need to do the same [string value] with the C program

You can generally do so in a double-quoted initializer, but there
are a couple of restrictions.

a) The double-quoted initializer wants
to tack a null on the end of the string, and it is not clear from
what you wrote before that that is acceptable; if not, then you cannot
use a double-quoted string (not unless you use it as a convenient
initializer but take a copy of it and strip off the trailing null
before actual use.)
Unless you declare a char array with an explicit bound which just fits
the contents of a string-literal initializer with no added null; in
this specific case no null is added, but this is error-prone
especially if you need (later) to change the string and its length.

Or, as you say later, initialize char[] with "string" which adds the
null, and then adjust the size, but you don't need to copy to do this.
Just e.g. write (fd1, msg, sizeof msg -1).
b) A sequence starting with \ extends as far as possible, so
the sequence \00000 wants to consume the five 0's, whereas the

No, \octal only takes up to three digits. And of course \b \r etc. do
exactly one letter. _Only_ \xhex takes as much as possible.

- David.Thompson1 at worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top