open full screen terminal app in another terminal - c

As an educational project, am I trying to write a simple terminal text editor. Below is a minimal example of my editor. My editor works fine when opening in the same terminal from which the program was run, however I would like to display the editor in a second terminal, leaving the original one free to print logging messages to. In the example below, the editor sucessfully opens in the second terminal, and the logging message about the number of rows and columns is printed in the original terminal. However, I can't work out how to get the editor to receive input from the second terminal, currently I need to close it by pressing ctrl+q in the orginal terminal. How can I get the editor to receive input from the second terminal?
I am compiling with gcc -Wall -Wextra -pedantic -std=c99 -g c_programs/open_alt_screen_in_other_term.c and running with ./a.out -tty /dev/pts/0
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#define ENTER_ALT_SCREEN "\033[?1049h"
#define EXIT_ALT_SCREEN "\033[?1049l"
#define MOVE_CURSOR_HOME "\033[H"
#define CTRL_KEY(k) ((k)&0x1f)
int UI_OUTPUT_FILENO = STDOUT_FILENO;
int UI_INPUT_FILENO = STDIN_FILENO;
struct termios orig_termios;
void disable_raw_mode();
void enable_raw_mode() {
tcgetattr(UI_INPUT_FILENO, &orig_termios);
atexit(disable_raw_mode);
struct termios raw = orig_termios;
raw.c_iflag &= ~(IXON | ICRNL);
raw.c_oflag &= ~(OPOST);
raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
raw.c_iflag &= ~(BRKINT | INPCK | ISTRIP);
raw.c_cflag |= (CS8);
tcsetattr(UI_INPUT_FILENO, TCSAFLUSH, &raw);
}
void disable_raw_mode() {
tcsetattr(UI_INPUT_FILENO, TCSAFLUSH, &orig_termios);
write(UI_OUTPUT_FILENO, EXIT_ALT_SCREEN, 8);
}
int getWindowSize(int *rows, int *cols) {
struct winsize ws;
if (ioctl(UI_OUTPUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
return -1;
} else {
*cols = ws.ws_col;
*rows = ws.ws_row;
return 0;
}
}
int main(int argc, char **argv) {
FILE *lfp = NULL;
for (int a = 1; a < argc; a++) {
if (strcmp(argv[a], "-tty") == 0) {
lfp = fopen(argv[a + 1], "w");
UI_OUTPUT_FILENO = fileno(lfp);
// UI_INPUT_FILENO = UI_OUTPUT_FILENO;
}
}
enable_raw_mode();
write(UI_OUTPUT_FILENO, ENTER_ALT_SCREEN, 8);
write(UI_OUTPUT_FILENO, MOVE_CURSOR_HOME, 3);
int nrows, ncols;
if (getWindowSize(&nrows, &ncols) == -1) return EXIT_FAILURE;
// print logging to original terminal
printf("rows: %d, cols: %d\n", nrows, ncols);
char seq[10] = {0};
while (read(UI_INPUT_FILENO, &seq, 10) != 0) {
if (seq[0] == CTRL_KEY('q')) {
break;
} else if (strlen(seq) == 1) {
write(UI_OUTPUT_FILENO, seq, 1);
}
for (size_t j = 0; j < 10; j++) seq[j] = '\0';
}
// if I clode lfp, the other terminal doesn't exit alt screen
// fclose(lfp);
return 0;
}

Related

How to read serial with interrupt serial?

I'm trying to read NMEA message in Linux. But I can't get a completely message:
54.441,V,,,,,0.00,0.00,010720,,,N*42
$GPVTG,0.00,T,,M,0.00,N,0.00,K,N*32
$GPGGA,020954.441,,,,,0,0,,,M,,M,,*43
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPGSV,1,1,00*79
$GLGSV,1,1,00*65
$GPGLL,,,,,020954.441,V,N*71
$GP
The first line and last line is the one message but it have been splited. I thing, It's cause by sleep 1 second. And It's not right at all. I think I should use interrupt serial.
My idea is when data in come, interrupt serial will run a function which read serial and handle it. After that, system will sleep until next message. I searched some material but It's doesn't help.
This is my new code and it's not working:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>
void signal_handler_IO ();
int fd;
int connected;
struct termios termAttr;
struct sigaction saio;
int main(int argc, char *argv[])
{
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
perror("open_port: Unable to open port\n");
exit(1);
}
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC );
tcgetattr(fd,&termAttr);
cfsetispeed(&termAttr,B9600);
cfsetospeed(&termAttr,B9600);
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
tcsetattr(fd,TCSANOW,&termAttr);
printf("UART1 configured....\n");
while(1){
sleep(1);
}
close(fd);
exit(0);
}
void signal_handler_IO ()
{
FILE *csv;
char buff [1024];
int n = read(fd, &buff, sizeof(buff));
char * token = strtok(buff, ",");
csv=fopen("csvfile.csv","w");
while( token != NULL ) {
fprintf(csv,"%s\n",token);
token = strtok(NULL, ",");
}
fclose(csv);
}
What should I do now ?
NMEA message are lines, ending with a '\n'.
Change read() to fgets() (open using fopen()) and read as a line into a string for later strtok() processing.
See also #Craig Estey ideas.
This is prefaced by my top comment.
thank you for your positive response. Did you mean I should use read() function like my old code ? And actually, I have never working with select before. But I very interesting with your idea. And I hope you can show me more the way which apply on my case.
Okay, here's a simple [and untested] version that does not use a signal handler. And, I'm using poll instead of select [they are similar] because it's easier to use.
Note that you opened the TTY device file with O_NDELAY, so the read call is non-blocking.
Also note that the open device will not produce an EOF condition either the way you did it or the way I'm doing it.
So, you'll have to have code that looks for a line that signifies the last line (e.g. $GP).
Or, after an initial wait the first time in the loop, a subsequent timeout should indicate no more input
Anyway, here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>
#if 1
#include <poll.h>
#endif
void signal_handler_IO(); /* definition of signal handler */
int fd;
struct termios termAttr;
struct sigaction saio;
struct pollfd fdpoll;
int
main(int argc, char *argv[])
{
int timout;
FILE *fout = NULL;
int buf_has_GP = 0;
int lastchr = -1;
int curchr;
int err;
int rlen;
int idx;
char buf[1000];
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open_port: Unable to open port\n");
exit(1);
}
#if 0
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO, &saio, NULL);
#endif
fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC);
tcgetattr(fd, &termAttr);
cfsetispeed(&termAttr, B9600);
cfsetospeed(&termAttr, B9600);
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
tcsetattr(fd, TCSANOW, &termAttr);
printf("UART1 configured....\n");
fout = fopen("csvfile.csv","w");
fdpoll.fd = fd;
fdpoll.events = POLLIN;
timout = 10000;
while (1) {
err = poll(&fdpoll,1,timout);
// timeout
if (err == 0)
break;
// error
if (err < 0) {
fprintf(stderr,"error -- %s\n",strerror(errno));
break;
}
// err will always be _one_ because poll's second arg is 1
while (1) {
rlen = read(fd,buf,sizeof(buf));
if (rlen <= 0)
break;
fwrite(buf,1,rlen,fout);
// need to check buf looking for last line (e.g. $GP)
// to know when to stop
// since read is _not_ line oriented we have to check for G followed
// by P [and they may or may not occur in the same read call]
// FIXME -- this is quite crude -- just to illustrate
for (idx = 0; idx < rlen; ++idx) {
curchr = buf[idx];
buf_has_GP = ((lastchr == 'G') && (curchr == 'P'));
if (buf_has_GP)
break;
lastchr = curchr;
}
if (buf_has_GP)
break;
}
if (buf_has_GP)
break;
timout = 1000;
#if 0
sleep(1);
#endif
}
close(fd);
fclose(fout);
exit(0);
}
void
signal_handler_IO()
{
FILE *csv;
FILE *ff;
char buff[1024];
ff = fopen("/dev/ttyUSB0", "r");
// int n = read(fd, &buff, sizeof(buff));
fgets(buff, sizeof(buff), ff);
char *token = strtok(buff, ",");
csv = fopen("csvfile.csv", "w");
while (token != NULL) {
fprintf(csv, "%s\n", token);
token = strtok(NULL, ",");
}
sleep(0.2);
fclose(csv);
}
UPDATE:
Thank you so much for spend your time for me. I compiled it without error. Unfortunately, I get nothing from output and empty file.
This may have been because of the simple/crude EOF string detection code. I think it could have stopped prematurely. I've added more robust checking.
I've also added debug printing (if -d is given).
Because I don't have access to a real ttyUSB device, I've added test code using a PTY [pseudo TTY]. To use this, put the sample NMEA data into a file (e.g. input.txt) and add -pinput.txt to the arguments.
This was how I was able to debug the general program flow.
I've turned off any unnecessary fcntl options.
If, after trying this, you still have issues, you may wish to test your device interface with a terminal program (e.g. minicom) to verify that the remote device is, indeed, sending data.
If minicom produces output, but your program doesn't, you may have to modify some of the termios options.
Some usbtty/uart devices need RTS/CTS [I've actually used such a device for work]. minicom has a config option to deal with this.
In the program [although I suspect it's off by default], you may need to disable RTS/CTS hardware so that the port doesn't get hung up. And/or ensure that XON/XOFF flow control is disabled.
Or, the remote device needs RTS/CTS support [you have to somehow force the remote device to see CTS high]. Although unlikely, this might have to be done in the cable itself.
Anyway, here's the updated code:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <sys/signal.h>
#include <errno.h>
#include <termios.h>
#if 1
#include <poll.h>
#include <pty.h>
#include <sys/wait.h>
#include <time.h>
#endif
#ifndef RAWOUT
#define RAWOUT 1
#endif
void signal_handler_IO(); /* definition of signal handler */
const char *ttydev = "/dev/ttyUSB0";
int fd;
int opt_d; // 1=debug print
char *opt_pty; // PTY input file
int ptypid;
#define PTYSLP 1
FILE *fout = NULL;
struct termios termAttr;
struct sigaction saio;
struct pollfd fdpoll;
int linelen;
char linebuf[1000];
#define SHOWPOLL(_msk) \
if (events & _msk) \
bp += sprintf(bp,"/" #_msk)
typedef long long tsc_t;
tsc_t
tscget(void)
{
struct timespec ts;
tsc_t tsc;
static tsc_t tsczero = 0;
clock_gettime(CLOCK_REALTIME,&ts);
tsc = ts.tv_sec;
tsc *= 1000000000;
tsc += ts.tv_nsec;
if (tsczero == 0)
tsczero = tsc;
tsc -= tsczero;
return tsc;
}
double
tscsec(tsc_t tsc)
{
double sec;
sec = tsc;
sec /= 1e9;
return sec;
}
void
tscprt(void)
{
tsc_t tsc;
tsc = tscget();
printf("%.9f ",tscsec(tsc));
}
#define dbgprt(_fmt...) \
do { \
if (! opt_d) \
break; \
int sverr = errno; \
tscprt(); \
printf(_fmt); \
errno = sverr; \
} while (0)
// dopty -- generate pseudo TTY test device
void
dopty(void)
{
int fdm;
int fdtxt;
int rlen;
int wlen;
int off;
char buf[1000];
#if 0
fdm = open("/dev/pts/ptmx",O_RDWR | O_NDELAY);
#else
fdm = getpt();
#endif
if (fdm < 0) {
perror("dopty/open");
exit(1);
}
dbgprt("dopty: GETPT fdm=%d\n",fdm);
ttydev = ptsname(fdm);
dbgprt("dopty: PTSNAME ttydev=%s\n",ttydev);
grantpt(fdm);
unlockpt(fdm);
dbgprt("dopty: INPUT opt_pty=%s\n",opt_pty);
do {
ptypid = fork();
if (ptypid != 0) {
close(fdm);
break;
}
// open sample test data file
fdtxt = open(opt_pty,O_RDONLY);
if (fdtxt < 0) {
perror("dopty/open");
exit(1);
}
sleep(PTYSLP);
while (1) {
rlen = read(fdtxt,buf,sizeof(buf));
if (rlen <= 0)
break;
dbgprt("dopty: READ rlen=%d\n",rlen);
for (off = 0; off < rlen; off += wlen) {
wlen = rlen - off;
wlen = write(fdm,&buf[off],wlen);
dbgprt("dopty: WRITE wlen=%d\n",wlen);
}
}
sleep(PTYSLP);
dbgprt("dopty: CLOSE\n");
close(fdtxt);
close(fdm);
sleep(PTYSLP);
dbgprt("dopty: EXIT\n");
exit(0);
} while (0);
}
char *
showpoll(short events)
{
char *bp;
static char buf[1000];
bp = buf;
bp += sprintf(bp,"%4.4X",events);
SHOWPOLL(POLLIN);
SHOWPOLL(POLLPRI);
SHOWPOLL(POLLOUT);
SHOWPOLL(POLLRDHUP);
SHOWPOLL(POLLERR);
SHOWPOLL(POLLHUP);
return buf;
}
// lineadd -- add character to line buffer
void
lineadd(int chr)
{
char *bp;
char buf[10];
if (opt_d) {
bp = buf;
*bp = 0;
if ((chr >= 0x20) && (chr <= 0x7E))
bp += sprintf(bp," '%c'",chr);
dbgprt("showchr: CHR chr=%2.2X%s\n",chr,buf);
}
linebuf[linelen++] = chr;
linebuf[linelen] = 0;
}
// eoftst -- decide if current line is the last line
int
eoftst(void)
{
static char *eofstr = "$GP\n";
static int eoflen = 0;
int stopflg = 0;
if (eoflen == 0)
eoflen = strlen(eofstr);
stopflg = ((linelen == eoflen) && (memcmp(linebuf,eofstr,eoflen) == 0));
dbgprt("eoftst: %s\n",stopflg ? "STOP" : "CONT");
return stopflg;
}
int
main(int argc, char **argv)
{
int timout;
int buf_has_eof = 0;
int curchr;
int err;
int rlen;
int idx;
char buf[1000];
--argc;
++argv;
setlinebuf(stdout);
setlinebuf(stderr);
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'd':
opt_d = ! opt_d;
break;
case 'p':
opt_pty = (*cp != 0) ? cp : "input.txt";
break;
}
}
do {
// create test device
if (opt_pty != NULL) {
dopty();
break;
}
if (argc > 0) {
ttydev = *argv;
--argc;
++argv;
}
} while (0);
dbgprt("main: TTYDEV ttydev=%s\n",ttydev);
fd = open(ttydev, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open_port: Unable to open port\n");
exit(1);
}
#if 0
saio.sa_handler = signal_handler_IO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO, &saio, NULL);
#endif
// not needed unless doing signal handler
#if 0
fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC);
#endif
#if 1
tcgetattr(fd, &termAttr);
#endif
#if 1
cfsetispeed(&termAttr, B9600);
cfsetospeed(&termAttr, B9600);
#endif
// force immediate return from device read if no chars available
#if 1
dbgprt("main: CC VMIN=%d VTIME=%d\n",
termAttr.c_cc[VMIN],termAttr.c_cc[VTIME]);
termAttr.c_cc[VMIN] = 0;
termAttr.c_cc[VTIME] = 0;
#endif
termAttr.c_cflag &= ~PARENB;
termAttr.c_cflag &= ~CSTOPB;
termAttr.c_cflag &= ~CSIZE;
termAttr.c_cflag |= CS8;
termAttr.c_cflag |= (CLOCAL | CREAD);
// FIXME -- you may need to handle this
#if 1
termAttr.c_cflag &= ~CRTSCTS;
#else
termAttr.c_cflag |= CRTSCTS;
#endif
termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
termAttr.c_oflag &= ~OPOST;
#if 1
tcsetattr(fd, TCSANOW, &termAttr);
#endif
printf("UART1 configured....\n");
// open output file
fout = fopen("csvfile.csv","w");
if (fout == NULL) {
perror("main/fopen");
exit(1);
}
fdpoll.fd = fd;
fdpoll.events = POLLIN;
fdpoll.revents = 0;
// set initial timeout of 10 seconds
timout = 10000;
// NOTE: iter is just for testing to prevent infinite looping if failure to
// read or match the EOF string
for (int iter = 1; iter < 10; ++iter) {
dbgprt("main: POLL iter=%d events=%s timout=%d\n",
iter,showpoll(fdpoll.events),timout);
err = poll(&fdpoll,1,timout);
dbgprt("main: POLL revents=%s err=%d\n",showpoll(fdpoll.revents),err);
// timeout
if (err == 0)
break;
// error
if (err < 0) {
fprintf(stderr,"error -- %s\n",strerror(errno));
break;
}
// err will always be _one_ because poll's second arg is 1
// process all data in current chunk
while (1) {
rlen = read(fd,buf,sizeof(buf));
dbgprt("main: READ iter=%d rlen=%d\n",iter,rlen);
if (rlen <= 0)
break;
// send data to output file
#if RAWOUT
fwrite(buf,1,rlen,fout);
#endif
// need to check buf looking for last line (e.g. $GP)
// to know when to stop
// since read is _not_ line oriented we have to check for G followed
// by P [and they may or may not occur in the same read call]
// FIXME -- this is quite crude -- just to illustrate
for (idx = 0; idx < rlen; ++idx) {
curchr = buf[idx];
// add to line buffer
lineadd(curchr);
// wait for newline
if (curchr != '\n')
continue;
// decide if this is the last line of the current NMEA message
buf_has_eof = eoftst();
#if (! RAWOUT)
// do processing on line buffer ...
#endif
// reset line buffer index/length for next line
linelen = 0;
if (buf_has_eof)
break;
}
if (buf_has_eof)
break;
}
if (buf_has_eof)
break;
// set 1 second timeout for subsequent reads
timout = 1000;
#if 0
sleep(1);
#endif
}
close(fd);
fclose(fout);
// reap any child processes [only if doing PTY mode]
while (opt_pty != NULL) {
pid_t pid = wait(NULL);
dbgprt("main: WAIT pid=%d\n",pid);
if (pid <= 0)
break;
}
exit(0);
}
void
signal_handler_IO()
{
FILE *csv;
FILE *ff;
char buff[1024];
ff = fopen("/dev/ttyUSB0", "r");
// int n = read(fd, &buff, sizeof(buff));
fgets(buff, sizeof(buff), ff);
char *token = strtok(buff, ",");
csv = fopen("csvfile.csv", "w");
while (token != NULL) {
fprintf(csv, "%s\n", token);
token = strtok(NULL, ",");
}
sleep(0.2);
fclose(csv);
}

Execute two functions at the same time independently in C

I think I may have a threading problem in c, but I'm not sure.
My goal is to execute two separate functions inside a while(1) loop as such:
One of these functions is kbget() to retrieve the key pressed in a terminal in non-canonical mode.
The second one is to constantly get the terminal window size with the ioctl(1, TIOCGWINSZ,...) function.
It normally doesn't work because the while(1) loop stops to get a keypress from the user before executing the second function to reevaluate the terminal window size. If the terminal window is resized before a key is pressed, the function to evaluate its size isn't executed unless a random key is pressed again.
In other words, resizing the terminal window doesn't update the size values in the Window struct below unless a key is pressed.
I want the program to update the y_size & x_size values 'live' as the terminal is resized.
Here's the issue in code without POSIX threads:
Executing with :
gcc -Wall scr.h main.c -o main && ./main
(scr.h below has kbget() to change terminal mode):
main.c:
#include "scr.h"
#include <sys/ioctl.h>
#define gotoyx(y, x) printf("\033[%d;%dH", (y), (x)) // equivalent to move(y, x) in ncurses
#define del_from_cursor(x) printf("\033[%dX", (x)) // delete x characters from cursor position
typedef struct {
int y_size;
int x_size;
} Window;
int main(void)
{
printf("\033[?1049h\033[2J\033[H"); // remember position & clear screen
gotoyx(1, 10);
printf("Press <ESC> to stop program.");
gotoyx(2, 10);
printf("Resizing the terminal window does not 'automatically' update the size shown on screen");
Window w;
struct winsize w_s;
while (1) {
// evaluate terminal size
if (!ioctl(1, TIOCGWINSZ, &w_s)) {
w.y_size = w_s.ws_row;
w.x_size = w_s.ws_col;
}
// print terminal size and center it
gotoyx(w.y_size / 2, w.x_size / 2);
del_from_cursor(5);
printf("w.y_size: %d", w.y_size);
gotoyx((w.y_size / 2) + 1, w.x_size / 2);
del_from_cursor(5);
printf("w.x_size: %d", w.x_size);
// get key pressed by user in terminal & exit if <ESC> is pressed
if (kbget() == 0x001b) { break; }
}
printf("\033[2J\033[H\033[?1049l"); // clear screen & restore
return 0;
}
I have tried solving this using threads but I was unsuccessful so far.
I have modified the main.c file above by adding 2 functions (get_window_size & get_key):
(scr.h has the kbget() function in get_key() to change the terminal to canonical mode)
main.c:
#include "scr.h"
#include <sys/ioctl.h>
#include <pthread.h>
#define gotoyx(y, x) printf("\033[%d;%dH", (y), (x))
#define del_from_cursor(x) printf("\033[%dX", (x))
typedef struct {
int y_size;
int x_size;
} Window;
void *get_window_size(void *arg)
{
Window *w = (Window *)arg;
struct winsize w_s;
if (!ioctl(1, TIOCGWINSZ, &w_s)) {
w->y_size = w_s.ws_row;
w->x_size = w_s.ws_col;
}
pthread_exit(0);
}
void *get_key(void *arg)
{
int *key = (int *)arg;
free(arg);
*key = kbget();
int *entered_key = malloc(sizeof(*key));
*entered_key = *key;
pthread_exit(entered_key);
}
int main(void)
{
printf("\033[?1049h\033[2J\033[H");
Window w;
pthread_t tid[3];
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&tid[0], &attr, get_window_size, &w);
int *c = malloc(sizeof(*c));
int *key_pressed;
while (1) {
// for initial size
pthread_join(tid[0], NULL);
// printing size to screen
gotoyx(w.y_size / 2, w.x_size / 2);
del_from_cursor(5);
printf("w.y_size: %d", w.y_size);
gotoyx((w.y_size / 2) + 1, w.x_size / 2);
del_from_cursor(5);
printf("w.x_size: %d", w.x_size);
// get window size
pthread_attr_t attr1;
pthread_attr_init(&attr1);
pthread_create(&tid[1], &attr1, get_window_size, &w);
// get key entered by user
pthread_attr_t attr2;
pthread_attr_init(&attr2);
pthread_create(&tid[2], &attr2, get_key, c);
pthread_join(tid[1], NULL);
pthread_join(tid[2], (void **)&key_pressed);
if (*key_pressed == 0x001b) {
break;
} else {
free(key_pressed);
}
}
if (key_pressed != NULL) {
free(key_pressed);
}
printf("\033[2J\033[H\033[?1049l");
return 0;
}
The scr.h file changes the terminal mode to non-canonical (the kbget() function above is called from here):
I don't think there's any problems in scr.h as it is taken from here (Move the cursor in a C program).
scr.h:
#ifndef SCR_H
#define SCR_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
struct termios term, oterm;
int getch(void)
{
int c = 0;
tcgetattr(STDIN_FILENO, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
return c;
}
int kbhit(void)
{
int c = 0;
tcgetattr(STDIN_FILENO, &oterm);
memcpy(&term, &oterm, sizeof(term));
term.c_lflag &= ~(ICANON | ECHO);
term.c_cc[VMIN] = 0;
term.c_cc[VTIME] = 1;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
c = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
if (c != -1) { ungetc(c, stdin); }
return c != -1 ? 1 : 0;
}
int kbesc(void)
{
int c = 0;
if (!kbhit()) { return 0x001b; } // 0x001b is the <ESC> key
c = getch();
if (c == 0) { while (kbhit()) { getch(); } }
return c;
}
int kbget(void)
{
int c = getch();
return c == 0x001b ? kbesc() : c; // 0x001b is the <ESC> key
}
#endif // SCR_H
I also get errors Invalid write of size 4 in the code above with pthread while executing with valgrind:
gcc -Wall scr.h main.c -pthread -o main
valgrind -v --leak-check=yes ./main
I am aware of the existence of ncurses and pdcurses. I am only doing this as an exercise for myself.
UPDATE
I have changed my code to the following, unfortunately the ret variable never changes to -1:
#include "scr.h"
#include <errno.h>
#include <sys/ioctl.h>
#define gotoyx(y, x) printf("\033[%d;%dH", (y), (x))
#define del_from_cursor(x) printf("\033[%dX", (x))
typedef struct {
int y_size;
int x_size;
} Window;
static int sigwinch_arrived = 0;
void sigwinch_handler(int signum)
{ sigwinch_arrived = 1; }
void on_window_size_change(Window *w)
{
struct winsize w_s;
// evaluate terminal size
if (!ioctl(1, TIOCGWINSZ, &w_s)) {
w->y_size = w_s.ws_row;
w->x_size = w_s.ws_col;
}
// print terminal size in its center
gotoyx(w->y_size / 2, w->x_size / 2);
del_from_cursor(15);
printf("w.y_size: %d", w->y_size);
gotoyx((w->y_size / 2) + 1, w->x_size / 2);
del_from_cursor(15);
printf("w.x_size: %d", w->x_size);
}
int main(void)
{
printf("\033[?1049h\033[2J\033[H");
gotoyx(1, 10);
printf("Press <ESC> to stop program.");
gotoyx(2, 10);
printf("Resizing the terminal window does not 'automatically' update the size shown on screen");
Window w;
int ret;
while (1) {
// get key pressed by user in terminal & exit if <ESC> is pressed
ret = kbget();
gotoyx(10, 10);
del_from_cursor(8);
printf("ret: %d", ret);
if (ret == -1) {
if (errno == EAGAIN) {
if (sigwinch_arrived) {
sigwinch_arrived = 0;
on_window_size_change(&w);
}
}
} else if (ret == 0x001b) {
break;
}
}
printf("\033[2J\033[H\033[?1049l");
return 0;
}
Extension: as per this answer, if your ncurses was compiled with the --enable-sigwinch flag, it does the solution below automatically (if you did not override SIGWINCH before ncurses_init() yet). In this case, getch() (wgetch()) will simply return KEY_RESIZE if a resize event is happened.
If the size of your controlling character terminal changes, your process should get a SIGWINCH signal (window size change, signal 28 on Linux).
It can be sent by the kernel (if there is mode switch on a character desktop), or by the virtual terminal software (xterm, gnome-terminal, screen, etc).
If your process gets a signal, its blocking kernel calls, including getch(), stop with the -EAGAIN error number. It means, that the blocking call stopped before time due to an arrived signal.
Note, from a signal handler, you can't do too much (for example: no malloc()), and the best to do if you make the least possible. Typical signal handlers change a static, global variable, whose value is checked by the main program.
Untested example code:
static int sigwinch_arrived = 0;
// this is called from the signal handler - nothing complex is allowed here
void sigwinch_handler(int signum) {
sigwinch_arrived = 1;
}
// callback if there is a window size change
void on_window_size_change() {
...
}
// main program
...
while (1) { // your main event handler loop
int ret = getch();
if (ret == ERR) {
if (errno == EAGAIN) {
if (sigwinch_arrived) {
sigwinch_arrived = 0;
on_window_size_change();
}
}
}
...
}

Serial monitor implementation segmentation fault

I have made a code to read the serialprint from an Arduino UNO and wish to use that to move the cursor in Linux Ubuntu 17.02. The code runs fine whenever it is run for 1 iteration.
My Arduino will print a code of the format
[0-1],xcord,ycord**
The star is padded so that the string is of length 12
xcord and ycord take values in between 0 and 1023
These values come from a joystick I wish I could be more specific on name or type of said joystick but it is not written on it any where
Moreover I doubt the problem has anything to do with my Arduino side but rather on my c side
Any help will be appreciated
#define _BSD_SOURC
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <string.h>
#include<unistd.h>
struct cord
{
int sw;
int x_axis;
int y_axis;
};
void mouseMove(struct cord s1)
{
Display *displayMain = XOpenDisplay(NULL);
if(displayMain == NULL)
{
fprintf(stderr, "Errore nell'apertura del Display !!!\n");
exit(EXIT_FAILURE);
}
//XWarpPointer(displayMain, None, None, 0, 0, 0, 0, s1.x_axis, s1.y_axis);
XCloseDisplay(displayMain);
}
struct cord decode(char *buffer)
{
struct cord s1;
s1.sw=buffer[0]-'0';
int i=2;
s1.x_axis=0;
s1.y_axis=0;
while(buffer[i]!=',')
{
s1.x_axis=s1.x_axis*10+(buffer[i]-'0');
i++;
}
i++;
while(buffer[i]!='*'||buffer[i]=='\0')
{
s1.y_axis=s1.y_axis*10+(buffer[i]-'0');
i++;
}
s1.x_axis=-s1.x_axis+497;
s1.y_axis=s1.y_axis-497;
//printf("%d %d %d\n",s1.sw,s1.x_axis/50,s1.y_axis/50);
return s1;
}
char* arread(int fd)
{
ssize_t n;
char* buf=(char *)malloc(128*sizeof(char));
n = read(fd, buf, 128);
buf[n]='\0';
//printf("%zd bytes got read...\n", n);
//printf("\n%s\n", buf);
return buf;
}
int main()
{
int fd;
char *portname = "/dev/ttyACM1";
if((fd = open(portname, O_RDWR | O_NOCTTY))==-1)
{
close(fd);
printf("error in opening Port");
}
else
{
struct termios toptions;
if(tcgetattr(fd, &toptions)==0)
{
if(cfsetispeed(&toptions, B9600)==0)
{
if(cfsetospeed(&toptions, B9600)==0)
{
toptions.c_cflag &= (unsigned int)~PARENB;
toptions.c_cflag &= (unsigned int)~CSTOPB;
toptions.c_cflag &= (unsigned int)~CSIZE;
toptions.c_cflag |= (unsigned int)CS8;
toptions.c_cflag &= (unsigned int)~CRTSCTS;
toptions.c_cflag |= (unsigned int)CREAD | (unsigned int)CLOCAL;
toptions.c_iflag &= (unsigned int)~(IXON | IXOFF | IXANY);
toptions.c_lflag &= (unsigned int)~(ICANON | ECHO | ECHOE | ISIG);
toptions.c_oflag &= (unsigned int)~OPOST;
toptions.c_cc[VMIN] = 12;
toptions.c_cc[VTIME] = 0;
if(tcsetattr(fd, TCSANOW, &toptions)==0)
{
//int i=0;
//while(i<5)
//{
mouseMove(decode(arread(fd)));
//i++;
//}
}
else
printf("error 4");
}
else
printf("error 3");
}
else printf("error 2");
}
else
printf("error 1");
}
}
This is the updated code i no longer get the error of segmentation fault however i keep on getting junk values as output . Also i dont know how it is printing as i blocked every printf that could do it.
For the value of the buffer this is the arduino code I used
const int SW_pin = 2; // digital pin connected to switch output
const int X_pin = 5; // analog pin connected to X output
const int Y_pin = 4; // analog pin connected to Y output
char buffer[12];
int x,n;
void setup()
{
pinMode(SW_pin, INPUT);
digitalWrite(SW_pin, HIGH);
Serial.begin(9600);
}
void loop()
{
n=sprintf(buffer,"%d,%d,%d",digitalRead(SW_pin),analogRead(X_pin),analogRead(Y_pin));
for(x=n;x<12;x++)
buffer[x]='*';
buffer[12]='\0';
Serial.println(buffer);
delay(500);
}
Finally the code worked thanks a lot to #user3629249
#define _BSD_SOURC
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termios.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <string.h>
#include<unistd.h>
struct cord
{
int sw;
int x_axis;
int y_axis;
};
void mouseMove(struct cord* s1)
{
Display *displayMain = XOpenDisplay(NULL);
if(displayMain == NULL)
{
fprintf(stderr, "Errore nell'apertura del Display !!!\n");
exit(EXIT_FAILURE);
}
//XWarpPointer(displayMain, None, None, 0, 0, 0, 0, s1.x_axis, s1.y_axis);
XCloseDisplay(displayMain);
}
struct cord* decode(char *buffer)
{
struct cord* s1 = malloc(sizeof(struct cord));
s1->sw=0;
s1->x_axis=0;
s1->y_axis=0;
if(strcmp(buffer,"error")!=1)
{
s1->sw=buffer[0]-'0';
int i=2;
while(buffer[i]!=',')
{
s1->x_axis=s1->x_axis*10+(buffer[i]-'0');
i++;
}
i++;
while(buffer[i]!='*'||buffer[i]=='\0')
{
s1->y_axis=s1->y_axis*10+(buffer[i]-'0');
i++;
}
s1->x_axis=-s1->x_axis+497;
s1->y_axis=s1->y_axis-497;
//printf("%d %d %d\n",s1.sw,s1.x_axis/50,s1.y_axis/50);
}
return s1;
}
char* arread(int fd)
{
ssize_t n,n1;
char* buf=(char *)malloc(128*sizeof(char));
char*ch=(char *)malloc(sizeof(char));
while(ch[0]!='\n')
{
n1=read(fd,ch,1);
}
n = read(fd, buf, 12);
buf[n]='\0';
printf("\n%zd\n", n);
printf("%s\n", buf);
if(n>0)
{
return buf;
}
else
{
printf("error");
return "error";
}
int main()
{
int fd;
char *portname = "/dev/ttyACM0";
fd = open(portname, O_RDWR | O_NOCTTY);
if(fd==-1)
{
close(fd);
printf("erip");
}
else
{
struct termios toptions;
if(tcgetattr(fd, &toptions)==0)
{
if(cfsetispeed(&toptions, B9600)==0)
{
if(cfsetospeed(&toptions, B9600)==0)
{
toptions.c_cflag &= (unsigned int)~PARENB;
toptions.c_cflag &= (unsigned int)~CSTOPB;
toptions.c_cflag &= (unsigned int)~CSIZE;
toptions.c_cflag |= (unsigned int)CS8;
toptions.c_cflag &= (unsigned int)~CRTSCTS;
toptions.c_cflag |= (unsigned int)CREAD | (unsigned int)CLOCAL;
toptions.c_iflag &= (unsigned int)~(IXON | IXOFF | IXANY);
toptions.c_lflag &= (unsigned int)~(ICANON | ECHO | ECHOE | ISIG);
toptions.c_oflag &= (unsigned int)~OPOST;
toptions.c_cc[VMIN] = 12;
toptions.c_cc[VTIME] = 0;
if(tcsetattr(fd, TCSANOW, &toptions)==0)
{
//int i=0;
//while(i<5)
//{
mouseMove(decode(arread(fd)));
//i++;
//}
}
else
printf("error 4");
}
else
printf("error 3");
}
else printf("error 2");
}
else
printf("error 1");
}
}
In the end the part that was causing all the errors was the read function. I originally used carelessly without thinking what the serial monitor is actually printing right now and so i got junk values . However after a ch searching for a '\n' it worked like a charm

I got a big trouble with BeagleboneBlack Uart

My beagleboneblack(let's call it "BBB") run 3.8.13-bone47, and I have been haunted by a bug for 2 weeks.
My problem is :when my BBB gets a string,my BBB will send the same string to another terminal.
For example:My laptop with a Uart2USB bridge sent a string "asd",then BBB will got "asd".But meanwhile, BBB would send "asd" to my laptop.And all of Uart module on BBB(Uart1,Uart2,Uart4) did the same thing.
By the way,I tried to avoid this bug by using two Uart(one for TX,another one for RX).Fortunately,I did it,but I still want to know the reason and the solution.
Here is my Uart.c,Uart.h and test.c:
/*====================Uart.c================*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <sys/time.h>
#include <string.h>
#include "Uart.h"
#define TRUE 1
#define FALSE -1
int fd_uart;
void set_speed(void)
{
int i;
int status;
int speed_arr = B38400;
struct termios Opt;
tcgetattr(fd_uart,&Opt);
tcflush(fd_uart, TCIOFLUSH);
cfsetispeed(&Opt, speed_arr);
cfsetospeed(&Opt, speed_arr);
status = tcsetattr(fd_uart, TCSANOW, &Opt);
if(status != 0)
perror("tcsetattr fd1");
}
int set_Parity(void)
{
struct termios options;
if( tcgetattr( fd_uart,&options)!= 0)
{
perror("SetupSerial 1");
return(FALSE);
}
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag &= ~PARENB; /* Clear parity enable */
options.c_iflag &= ~INPCK; /* Enable parity checking */
options.c_iflag &= ~(ICRNL|IGNCR);
options.c_lflag &= ~(ICANON );
options.c_cflag &= ~CSTOPB;
options.c_iflag |= INPCK;
options.c_cc[VTIME] = 150; // 15 seconds
options.c_cc[VMIN] = 0;
tcflush(fd_uart,TCIFLUSH); /* Update the options and do it NOW */
if(tcsetattr(fd_uart,TCSANOW,&options) != 0)
{
perror("SetupSerial 3");
return (FALSE);
}
return (TRUE);
}
void initUart(int argc, char **argv)
{
char devname_head[10] = "/dev/";
char dev_name[20];
if(argc < 2)
{
printf("Please input './test_uart ttyOx'\n");
exit(1);
} else {
strcpy(dev_name, devname_head);
strcat(dev_name, argv[1]);
fd_uart = open(dev_name, O_RDWR);
if(fd_uart < 0)
{
perror("error to open /dev/ttyOx\n");
exit(1);
} else if (fd_uart == 0) {
printf("Can't Open Serial Port!\n");
exit(0);
} else {
//Setup
set_speed(); //baud:38400
if (set_Parity() == FALSE) //8,1,n
{
printf("Set Parity Error\n");
exit(1);
}
}
}
}
void writeUartString(char *buf)
{
if ( write(fd_uart,buf,strlen(buf)) < 0)
{
printf("write error\n");
}
}
void writeUartMsg(char *buf,int num)
{
if ( write(fd_uart,buf,num) < 0)
{
printf("write error\n");
}
}
int readUart(char *buf)
{
int num = 0, i = 0;
num = read(fd_uart,buf,sizeof(buf));
if(num < 0){
printf("read error\n");
}
return num;
}
void closeUart(void)
{
close(fd_uart);
}
/*======================================================*/
/*==================Uart.h===================*/
#ifndef UART_H
#define UART_H
void set_speed(void);
int set_Parity(void);
void initUart(int argc, char **argv);
void writeUartString(char *buf);
void writeUartMsg(char *buf,int num);
void closeUart(void);
extern int fd_uart;
#endif
/*=======================================*/
/*================test.c=================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Uart.h"
int main(int argc, char **argv)
{
int i = 0,temp = 0;
char buf[64] = {0};
initUart(argc,argv);
while(1){
writeUartString("waiting\n");
printf("waiting\n");
memset(buf,0,64);
temp = readUart(buf);
printf("readUart's return value = %d\n",temp);
if (temp > 0){
printf("get something\n");
printf("%s\n",buf);
}
sleep(1);
}
return 0;
}
/*====================================*/
Did somebody get this situation too?
You left echoing enableed with
options.c_lflag &= ~(ICANON );
Change it, at least, to
options.c_lflag &= ~(ICANON | ECHO );
With you are left at their original state all flags, rather then except that ICANON.

sending AT command to teltonika GSM modem using C in ubuntu 12.04

Hi I am trying to send AT commands to teltonika gsm modem using com port(ttyS0) of my ubuntu desktop. The problem is instead of getting "OK" reply to the AT command ( or any other command or string), the same AT command is getting echo'ed back. Any help in this regard will be appreciated. Here's the C code m working on:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#define BAUDRATE B115200
#define COM1 "/dev/ttyS0"
static int fd;
static struct termios oldtio,newtio;
//==============================================================
int tty_read(char *buf1,int nbytes)
{
int temp;
temp = read(fd,buf1,nbytes);
printf("Read string: %s\n", buf1);
return temp;
}
//==============================================================
int tty_end()
{
tcsetattr(fd,TCSANOW,&oldtio);
close(fd);
}
//==============================================================
int tty_writecmd(char *buf,int nbytes)
{
write(fd,buf,nbytes);
usleep(1000);
return tcdrain(fd);
}
//==============================================================
int baud = B115200;
int tty_init()
{
fd = open(COM1, O_RDWR );
if (fd <0) {
perror(COM1);
exit(1);
}
tcgetattr(fd,&oldtio);
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = baud | CRTSCTS | CS8 | CLOCAL | CREAD ;
newtio.c_iflag = IGNPAR | ICRNL;
newtio.c_oflag = 0;
newtio.c_lflag = ICANON;
newtio.c_cc[VINTR] = 0;
newtio.c_cc[VQUIT] = 0;
newtio.c_cc[VERASE] = 0;
newtio.c_cc[VKILL] = 0;
newtio.c_cc[VEOF] = 4;
newtio.c_cc[VTIME] = 0;
newtio.c_cc[VMIN] = 1;
newtio.c_cc[VSWTC] = 0;
newtio.c_cc[VSTART] = 0;
newtio.c_cc[VSTOP] = 0;
newtio.c_cc[VSUSP] = 0;
newtio.c_cc[VEOL] = 0;
newtio.c_cc[VREPRINT] = 0;
newtio.c_cc[VDISCARD] = 0;
newtio.c_cc[VWERASE] = 0;
newtio.c_cc[VLNEXT] = 0;
newtio.c_cc[VEOL2] = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&newtio);
return 0;
}
int main(int argc, char *argv[])
{ int wr,rd;
char *buff;
char recv[15];
char command[] = "AT\r\n";
tty_init();
printf("Write: %d\n", tty_writecmd(command, sizeof(command)));
usleep(10000);
printf("Read: %d\n", tty_read(recv ,sizeof(recv)));
tty_end();
}
and the output is like
write:0
Read string: AT
Read:3
Thanks
P.S : This behaviour occurs in Ubuntu desktop, the program reads nothing from serial port in VMware station.
instead of usleep(10000);
use
usleep(1000);

Resources