New line character significance in serial port communication - c

We are testing serial port communication. There are two tty nodes /dev/ttyUSB8 and /dev/ttyUSB9 on the device.
When I transmit buffer from /dev/ttyUSB8 to /dev/ttyUSB9 I don't receive data on the /dev/ttyUSB9 read call if the buffer doesn't contain new line.
Transmit Code
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
void write_func()
{
int fdw;
int i;
fdw = open("/dev/ttyUSB8", O_RDWR);
printf("fdw : %d\n",fdw);
printf("%ld\n", write(fdw, "Hello", 6));
close(fdw);
}
int main()
{
int i;
write_func();
return 0;
}
Receive Code
void read_thread()
{
int fdr;
char buf[] = "NoData";
fdr = open("/dev/ttyUSB9", O_RDWR);
printf("fdr : %d\n",fdr);
printf("%s: %ld\n", __func__, read(fdr, buf, 6));
printf("%s\n", buf);
close(fdr);
}
int main()
{
int i;
read_thread();
return 0;
}
I don't receive data with the above call, but when I add '\n in the write call i get data in the read block call.
printf("%ld\n", write(fdw, "Hello\n", 7));
What is the significance of new line character in this..
Update:
I added the code to reset canonical mode, still it didn't work:
void write_thread()
{
int fdw;
int i;
struct termios config;
fdw = open("/dev/ttymib24", O_RDWR);
printf("fdw : %d\n",fdw);
tcgetattr(fdw, &config);
config.c_lflag &= ~ICANON;
tcsetattr(fdw, TCSANOW, &config);
printf("%ld\n", write(fdw, "Hello", 6));
close(fdw);
}

Your tty is probably in canonical mode.
Try to reset ICANON by using tcsetattr(). Something like this:
struct termios termiosv;
tcgetattr(fd, &termiosv);
termiosv.c_lflag &= ~ICANON;
tcsetattr(fd, TCSANOW, &termiosv);
More information in man page of termios:
In canonical mode:
* Input is made available line by line. An input line is available
when one of the line delimiters is typed (NL, EOL, EOL2; or EOF at
the start of line). Except in the case of EOF, the line delimiter
is included in the buffer returned by read(2).

Related

How can I take backspace character in a char array in C language and execute an IF condition on basis of Backspace Character?

My sample code is as follows:
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_LIMIT 20
int main()
{
char str[10];
printf("\nEnter any character");
fgets(str, 10, stdin);
***if(str[0] == '\b'){
"Function call here"
printf("\nbackspace is entered");***
printf("%s", str);
//Below part is for string to int conversion//
int integer;
sscanf(str, "%d", &integer);
printf("\nNew integer is %d",integer);
return 0;
My main problem is in the if condition .... Please guide me....I'll be very thankful to all of you
In canonical mode of terminal drivers (also known as "cooked" mode), control characters are processed before being passed to the program. See termios for more information.
You can however, disable the control chars you are interested. For example, you can disable DEL to get backspaces:
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
int main()
{
struct termios old;
/* Get current settings. */
tcgetattr(STDIN_FILENO, &old);
struct termios new = old;
new.c_lflag = ICANON;
/* Disable DEL. */
new.c_cc[VERASE] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &new);
char str[10] = {0};
printf("\nEnter:");
fgets(str, 10, stdin);
if(str[0] == 127) {
printf("backspace is entered\n");
}
/* Restore old settings. */
tcsetattr(STDIN_FILENO, TCSANOW, &old);
}

Why is Linux's pty driver replacing VEOFs with NULs?

It seems that the pty driver on Linux is replacing VEOF characters (^D, \4) with NUL bytes (\0) in the data already written from the master side if the terminal settings are changed with tcsetattr(TCSANOW) to non-canonical mode before reading it on the slave side.
Why is this happening? Does it have any justification or it's simply a bug?
Is there any way to avoid it? -- other than waiting for input from the slave on the master side before writing anything, which is not practical, because on the slave side there may be another program -- the routine of setting the terminal into raw mode which I've simplified here is usually what any shell with line-editing capabilities does.
While having eg \r replaced by \n could be expected (because the ICRNL flag was already applied), I cannot make any rationale for those NUL bytes appearing out of nowhere.
Test case below: it will print foo\x00\x00\x00\x00 on Linux, but foo\x04\x04\x04\x04 on *BSD and foo on Solaris.
#define _XOPEN_SOURCE 600
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdio.h>
#include <termios.h>
#include <err.h>
#ifdef __sun
#include <stropts.h>
#define push_streams(fd)\
if(ioctl(fd, I_PUSH, "ptem")) err(1, "ioctl(I_PUSH, ptem)");\
if(ioctl(fd, I_PUSH, "ldterm")) err(1, "ioctl(I_PUSH, ldterm)");
#else
#define push_streams(sd) /* no need */
#endif
int main(void){
int mt, st; char *sname;
/* openpty()-like boilerplate */
if((mt = posix_openpt(O_RDWR|O_NOCTTY)) == -1) err(1, "posix_openpt");
if(grantpt(mt)) err(1, "grantpt");
if(unlockpt(mt)) err(1, "unlockpt");
if(!(sname = ptsname(mt))) err(1, "ptsname");
if((st = open(sname, O_RDWR|O_NOCTTY)) == -1) err(1, "open %s", sname);
push_streams(st);
/* master */ {
char test[] = "foo\4\4\4\4";
if(write(mt, test, sizeof test - 1) < sizeof test - 1)
err(1, "write");
}
/* slave */ {
unsigned char buf[512]; int i, r;
struct termios ts;
usleep(1000);
if(tcgetattr(st, &ts)) err(1, "tcgetattr");
ts.c_lflag &= ~ICANON;
if(tcsetattr(st, TCSANOW, &ts)) err(1, "tcsetattr");
if((r = read(st, buf, sizeof buf)) < 0)
err(1, "read");
for(i = 0; i < r; i++)
if(isprint(buf[i])) putchar(buf[i]);
else printf("\\x%02x", buf[i]);
putchar('\n');
}
return 0;
}
This conversion is done by the line discipline driver when the data is written by the master, not when the slave reads the data. The relevant code is:
https://elixir.bootlin.com/linux/latest/source/drivers/tty/n_tty.c#L1344

Serial I/O in C with termios: unreliable output capitalization

I have a very small C program which sends and receives newline-terminated ASCII strings to and from a serial device. It's plugged into my computer with a USB adapter, on /dev/ttyUSB0.
Most of the time it sends the commands just find, but occasionally it will capitalize all the lower-case letters to upper-case. It leaves all special characters alone.
The string I am sending is /home\n. About 1 out of every five times I run the program (by simply running ./a.out without recompiling), the sent message understood by the device is /HOME\n.
Here is my source code:
#include <stdio.h>
#include <stdlib.h>
#include "zserial.h"
int main() {
char buf[256];
int fd = connect("/dev/ttyUSB0");
char *cmd = "/home\n";
send(fd, cmd);
receive(fd, buf, 256);
puts(buf);
exit(0);
}
And zserial.c:
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zserial.h"
int send(int fd, char *buf) {
int len = strlen(buf);
int nb = write(fd, buf, len);
if (len != nb || nb < 1)
perror("Error: wrote no bytes!");
tcdrain(fd);
return nb;
}
int receive(int fd, char *dst, int nbytes) {
int i;
char c;
for(i = 0; i < nbytes;) {
int r = read(fd, &c, 1);
/* printf("Read %d bytes\n", r); */
if (r > 0) {
dst[i++] = c;
if (c == '\n') break;
}
}
dst[i] = 0; /* null-terminate the string */
return i;
}
int connect(char *portname) {
int fd;
struct termios tio;
fd = open(portname, O_RDWR | O_NOCTTY | O_NONBLOCK);
tio.c_cflag = CS8|CREAD|CLOCAL;
if ((cfsetospeed(&tio, B115200) & cfsetispeed(&tio, B115200)) < 0) {
perror("invalid baud rate");
exit(-1);
}
tcsetattr(fd, TCSANOW, &tio);
return fd;
}
What am I doing wrong? Is there some termios flag which modifies the output on a serial port?
c_oflag & OLCUC turns on the mapping of lowercase to uppercase on output. Since you never initialized tio, it's not surprising you got some random flags set.
You have two choices:
tcgetattr the current settings into a termios struct to initialize it, then modify the ones you're interested in, then write them back with tcsetattr
initialize all the termios fields to known values, not just c_cflag and the speed fields.

C termios and printf issue

I'm using Lubuntu with LXterminal.
I have (somewhat) unashamedly copied the basis for this code from a stack overflow answer that gives details on c non-blocking keyboard input.
This is the first part:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <termios.h>
using namespace std;
struct termios orig_termios;
void reset_terminal_mode()
{
tcsetattr(0, TCSANOW, &orig_termios);
}
void set_conio_terminal_mode()
{
struct termios new_termios;
/* take two copies - one for now, one for later */
tcgetattr(0, &orig_termios);
memcpy(&new_termios, &orig_termios, sizeof(new_termios));
/* register cleanup handler, and set the new terminal mode */
atexit(reset_terminal_mode);
cfmakeraw(&new_termios);
tcsetattr(0, TCSANOW, &new_termios);
}
int kbhit()
{
struct timeval tv = { 0L, 0L };
fd_set fds;
FD_ZERO(&fds);
FD_SET(0, &fds);
return select(1, &fds, NULL, NULL, &tv);
}
int getch()
{
int r;
unsigned char c;
if ((r = read(0, &c, sizeof(c))) < 0) {
return r;
} else {
return c;
}
}
Here is one main function that shows some strange behavior.
int main(int argc, char *argv[])
{
unsigned int stor;
set_conio_terminal_mode();
for(int i = 0; i < 6; i++){
while (!kbhit()) {} /* wait */
stor = getch(); /* consume the character */
reset_terminal_mode();
printf("\033[38;33m%i \033[38;0m", stor);
set_conio_terminal_mode();
}
printf("more text\n");
}
What this main loop does is it gets 6 character blocks (ex. ENTER 6 times or arrow key twice.) However, where it says printf there is no printout until the program finishes.
This can be seen better when you add
while(1){}
to the end of the main function.
So what's going on here? Is there some sort of magic that happens at the end of the program that releases all of the printf functions?
How do I make it printf when the program is still running?
Apparently, you are victim of excessive buffering.
Try to disable buffering using setvbuf.
To completely disable buffering on stdout:
setvbuf(stdout, (char *)NULL, _IONBF, 0);
To enable buffering for each line:
setvbuf(stdout, (char *)NULL, _IOLBF, 0);
// or
setlinebuf(stdout);

How to handle key pressed in a Linux console in C?

I'm using Linux console and I would like to do a program which outputs random characters until ESC is pressed. How can I make such a keyboard handler?
The line discipline for a terminal device often works in canonical mode by default. In this mode, the terminal driver doesn't present the buffer to userspace until the newline is seen (Enter key is pressed).
You can set the terminal into raw (non-canonical) mode by using tcsetattr() to manipulate the termios structure. Clearing the ECHO and ICANON flags respectively disables echoing of characters as they are typed and causes read requests to be satisfied directly from the input queue. Setting the values of VTIME and VMIN to zero in the c_cc array causes the read request (fgetc()) to return immediately rather than block; effectively polling stdin. The call to fgetc() will return EOF if a character is not available in the stream.
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>
int getkey() {
int character;
struct termios orig_term_attr;
struct termios new_term_attr;
/* set the terminal to raw mode */
tcgetattr(fileno(stdin), &orig_term_attr);
memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios));
new_term_attr.c_lflag &= ~(ECHO|ICANON);
new_term_attr.c_cc[VTIME] = 0;
new_term_attr.c_cc[VMIN] = 0;
tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);
/* read a character from the stdin stream without blocking */
/* returns EOF (-1) if no character is available */
character = fgetc(stdin);
/* restore the original terminal attributes */
tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr);
return character;
}
int main()
{
int key;
/* initialize the random number generator */
srand(time(NULL));
for (;;) {
key = getkey();
/* terminate loop on ESC (0x1B) or Ctrl-D (0x04) on STDIN */
if (key == 0x1B || key == 0x04) {
break;
}
else {
/* print random ASCII character between 0x20 - 0x7F */
key = (rand() % 0x7F);
printf("%c", ((key < 0x20) ? (key + 0x20) : key));
}
}
return 0;
}
Note: This code omits error checking for simplicity.
change the tty settings for one key press:
int getch(void) {
int c=0;
struct termios org_opts, new_opts;
int res=0;
//----- store old settings -----------
res=tcgetattr(STDIN_FILENO, &org_opts);
assert(res==0);
//---- set new terminal parms --------
memcpy(&new_opts, &org_opts, sizeof(new_opts));
new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
c=getchar();
//------ restore old settings ---------
res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
assert(res==0);
return(c);
}
getch() from Curses library perhaps? Also, you will need to use notimeout() to tell getch() not to wait for next keypress.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
char * me = "Parent";
void sigkill(int signum)
{
//printf("=== %s EXIT SIGNAL %d ===\n", me, signum);
exit(0);
}
main()
{
int pid = fork();
signal(SIGINT, sigkill);
signal(SIGQUIT, sigkill);
signal(SIGTERM, sigkill);
if(pid == 0) //IF CHILD
{
int ch;
me = "Child";
while(1)
{
ch = (rand() % 26) + 'A'; // limit range to ascii A-Z
printf("%c",ch);
fflush(stdout); // flush output buffer
sleep(2); // don't overwhelm
if (1 == getppid())
{
printf("=== CHILD EXIT SINCE PARENT DIED ===\n");
exit(0);
}
}
printf("==CHILD EXIT NORMAL==\n");
}
else //PARENT PROCESS
{
int ch;
if((ch = getchar())==27)
kill(pid, SIGINT);
//printf("==PARENT EXIT NORMAL (ch=%d)==\n", ch);
}
return(0);
}
In this program u will only need to press enter after esc char,because getchar()is a blocking function.
Also u may remove or decrease sleep time for child process as ur need.

Resources