I'm trying to write a program to interpret traffic received from a serial interface under Cygwin. I copied some demo code from another project to open the serial port. Then I set up the serial port speed to the baudrate used by the serial port (921600).
static int serial_open(char *portname)
{
int FD_com; // file descriptor for the serial port
struct termios term;
FD_com = open(portname, O_RDONLY | O_NOCTTY | O_NDELAY);
if (FD_com == -1) // if open is unsuccessful
{
printf("serial_open: Unable to open %s.\n", portname);
}
else
{
// set speed of port
cfsetspeed(&term, BAUDRATE);
// set to 8-bits, no parity, 1 stop bit
term.c_cflag &= ~PARENB;
term.c_cflag &= ~CSTOPB;
term.c_cflag &= ~CSIZE;
term.c_cflag |= CS8;
term.c_cflag |= (CLOCAL | CREAD);
tcsetattr(FD_com, TCSANOW, &term);
}
return(FD_com);
}
Then I tried to capture the traffic in a file
if (FD_ISSET(FD_com, &fds))
{
// wait for data to come in on the serial port
if ((nbytes = read(FD_com, port_buf, PORTBUFSIZE)) > 0)
{
for (i=0; i<nbytes; i++)
{
fwrite(&port_buf[i], 1, sizeof(uint8_t), fout);
The problem is, the traffic recorded in the file lost some bytes. The longest consecutive string it can record is over 48 bytes, but most other strings, it wraps around with 48 bytes. I assume that this because of buffer overflow. But how to configure the serial port rx buffer? The port_buf defined in the application is 512 (PORTBUFSIZE)bytes, which should be big enough.
Related
I am reading the data from the serial/UART, with 9600 baud rate (my 115200 baud rate is working fine)
I wrote the below code, but the select API every time gives time out, it requires timeout more than 2 seconds, that is not desirable. As I have 2048 bytes of data so within 100 ms I should able to get some data, but I think select API is not receiving interrupts, even though Rx buffer of serial has some data to process.
NOTE: the same piece of code working fine on beaglebone black, while not on intel atom-3900 / Kernel used is Linux-intel 4.9.
Thanks for the anticipation
code:
int main(void)
{
int ret;
char buf[1280];
fd_set m_Inputs; // fd_set for the select call for com input
int m_maxFD; // max FD for select() call.
struct timeval tv;
/*Open Serial port in non blocking mode*/
int fd1;
fd1 = open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NONBLOCK);
/* get the termios structure */
struct termios options;
tcgetattr(fd1, &options); // Get the current options for the port...
// Set the baud rates...
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
// Enable the receiver and set local mode...
options.c_cflag |= (CLOCAL | CREAD | CS8);
options.c_cflag &= ~PARENB; // ignore parity
options.c_cflag &= ~CSTOPB; // 1 stop bit (2 if set)
options.c_cflag &= ~CSIZE; // clear the size bits
options.c_cflag &= ~CRTSCTS; //No hard flow control
options.c_cflag &= ~HUPCL; //Hang Up on last Close
options.c_cflag |= CS8; // reset the size to 8 bits / char
options.c_cc[VMIN]=1;
options.c_cc[VTIME] = 1;
options.c_oflag = 0;
options.c_lflag = 0; //ICANON;
// Set the new options for the port...
tcsetattr(fd1, TCSANOW, &options);
cout << endl << "FD1 = " << fd1 << endl;
while (1)
{
fd_set rfds; // fd_set for the select call for com input
FD_ZERO(&rfds);
FD_SET(fd1, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 100000;
ret = select(fd1 + 1, &rfds, NULL, NULL, &tv);
if (ret > 0)
{
ret = read(fd1, buf, 127);
buf[ret] = '\0';
cout << buf;
}else{
cout << "Time OUT" << endl;
break;
}
}
return 0;
}
Good afternoon,
I have a peripheral device which communicates over usb over virtual serial port. Everything works well under Windows with generic ACM serial driver, for example with: https://www.kernel.org/doc/Documentation/usb/linux-cdc-acm.inf
Under Linux, it uses CDC ACM drivers. Everything in sys logs seems to work ok, but communication is behaving strangely. When I connect the device, about 10 bytes at the begining of communication are lost. Next, just each second command is received ok.
My questions are:
1) Communication protocol of this device doesn't use ASCII, it is binary (it can randomly contain control characters etc...). Should I use stty for configuring just speed, data bits, stop bits and parity, or something more is necessary to be set up for binary communication? (To ignore control bits in kernel and transmit every byte - raw data.)
2) Any idea, how to test if linux ACM drivers works properly, or which another drivers should i try for my CDC ACM device?
Thanks for any idea!
Linux will often mangle things like line-ending characters (0x0A and 0x0D) when you try to send them over a serial port, which can cause issues if they are actually binary data and not intended as line-ending characters.
Here is a snippet from Pololu that shows how to configure your serial port correctly and then send and receive a few bytes. Pay attention to the part that calls tcsetattr in particular.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#ifdef _WIN32
#define O_NOCTTY 0
#else
#include <termios.h>
#endif
// Gets the position of a Maestro channel.
// See the "Serial Servo Commands" section of the user's guide.
int maestroGetPosition(int fd, unsigned char channel)
{
unsigned char command[] = {0x90, channel};
if(write(fd, command, sizeof(command)) == -1)
{
perror("error writing");
return -1;
}
unsigned char response[2];
if(read(fd,response,2) != 2)
{
perror("error reading");
return -1;
}
return response[0] + 256*response[1];
}
// Sets the target of a Maestro channel.
// See the "Serial Servo Commands" section of the user's guide.
// The units of 'target' are quarter-microseconds.
int maestroSetTarget(int fd, unsigned char channel, unsigned short target)
{
unsigned char command[] = {0x84, channel, target & 0x7F, target >> 7 & 0x7F};
if (write(fd, command, sizeof(command)) == -1)
{
perror("error writing");
return -1;
}
return 0;
}
int main()
{
const char * device = "/dev/ttyACM0"; // Linux
int fd = open(device, O_RDWR | O_NOCTTY);
if (fd == -1)
{
perror(device);
return 1;
}
#ifdef _WIN32
_setmode(fd, _O_BINARY);
#else
struct termios options;
tcgetattr(fd, &options);
options.c_iflag &= ~(INLCR | IGNCR | ICRNL | IXON | IXOFF);
options.c_oflag &= ~(ONLCR | OCRNL);
options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tcsetattr(fd, TCSANOW, &options);
#endif
int position = maestroGetPosition(fd, 0);
printf("Current position is %d.\n", position);
int target = (position < 6000) ? 7000 : 5000;
printf("Setting target to %d (%d us).\n", target, target/4);
maestroSetTarget(fd, 0, target);
close(fd);
return 0;
}
You might be able to do the same thing with the stty command line utility.
I have created a bluetooth input device (stylus) and would like to connect it to both a Mac and Windows (and preferably Linux in the future).
Is there an ideal software / language to use to create a cross-platform application? I have considered writing native applications for each, but I don't feel the application will be so complex that this is absolutely necessary.
The application will take the input data of the BT device and use it to move the cursor around the screen and provide click and pressure functionality.
Thank you in advance.
I don't know how you device is set up.
However, if you managed to put on it a PIC (Such as the Arduino ATMega328) with at least one serial interface, you could be able to connect it to your PC via Universal Serial Bus (USB).
After that you will be able to open a pipe to your device in many languages.
C is always a good choice both for Linux and OS X, using POSIX libraries will make it even easier.
This snippet I wrote taking some tips online may help to get started
int init_port (const char * port_name, int baud) {
/* Main vars */
struct termios toptions;
int stream;
/* Port data */
speed_t brate = baud;
if ((stream = apri_porta(port_name)) < 1)
return 0;
if (tcgetattr(stream, &toptions) < 0) {
printf("Error");
return 0;
}
/* INITIALIZING BAUD RATE */
cfsetispeed(&toptions, brate);
cfsetospeed(&toptions, brate);
// IMPORTANT BLOCK OF OPTIONS TO MAKE TX AND RX WORKING
toptions.c_cflag &= ~PARENB;
toptions.c_cflag &= ~CSTOPB;
toptions.c_cflag &= ~CSIZE;
toptions.c_cflag |= CS8;
toptions.c_cflag &= ~CRTSCTS;
toptions.c_cflag |= CREAD | CLOCAL;
toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
toptions.c_oflag &= ~OPOST;
toptions.c_cc[VMIN] = 0;
toptions.c_cc[VTIME] = 0;
tcsetattr(stream, TCSANOW, &toptions);
if (tcsetattr(stream, TCSAFLUSH, &toptions) < 0) {
printf("Error");
return 0;
}
return stream;
}
int open_port (const char * port_name) {
int stream;
stream = open(port_name, O_RDWR | O_NONBLOCK );
if (stream == -1) {
printf("apri_porta: Impossibile aprire stream verso '%s'\n", port_name);
return -1;
}
return stream;
}
int close_port (int stream) {
return (close(stream));
}
int write_to_port(int stream, char * str) {
int len = (int)strlen(str);
int n = (int)write(stream, str, len);
if (n != len)
return 0;
return 1;
}
int read_from_port(int fd, char * buf, int buf_max, char until) {
int timeout = 5000;
char b[1];
int i=0;
do {
int n = (int)read(fd, b, 1);
if( n==-1) return -1;
if( n==0 ) {
usleep( 1 * 1000 );
timeout--;
continue;
}
buf[i] = b[0];
i++;
} while( b[0] != until && i < buf_max && timeout > 0 );
buf[i] = 0; // null terminate the string
return 0;
}
Objective-C (OS X) has got a good library which works like a charm (ORSSerialPort)
However, if you would like to have a cross-platform solution, Java is the best choice either for Windows, OS X and Linux.
I hope this helped you and others to get started.
Feel free to PM me if you need further help.
Best regards.
I try to read out a serial device. I am working on an Ubuntu 12.04. The device is sending in equivalent time distances some Bits.
I connect to the device via Hterm and I can see the correctly incoming data.
If I read out the device via the linux command cat, there are several Bits missing.
My third method was to write a little C-file
struct termios options;
int main(void)
{
int fd; /* File descriptor for the port */
fd = open(DEVICE, O_RDWR | O_NONBLOCK);
if (fd < 0)
{
/*
* Could not open the port.
*/
perror("open_port: Unable to open /dev/ttyACM3 - ");
}
fd_set read_fds, write_fds, except_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&except_fds);
FD_SET(fd,&read_fds);
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
if(select(fd + 1, &read_fds, &write_fds, &except_fds, &timeout) == 1){
char replay[DEBUG_REPLAY_MAX_SIZE];
memset(replay, 0x00, DEBUG_REPLAY_MAX_SIZE);
read(fd, &replay, DEBUG_REPLAY_MAX_SIZE);
printf("Daten: %s", replay);
}
else{
puts("Timeout");
}
return (fd);
}
This code should do the following:
- open serial device and return file descriptor
- the function select() waits a maximum time of 10 s listening there are comming some data in
But if I execute the c-file do not wait 10 s, but prints only "Daten:" with no following datas. Really strange.
Can someone explain this behaviour?
Thanks a lot,
Florian
I am writing a linux application in the userspace to communicate to an FPGA via uart. I am using non-canonical blocking read operation with vmin and vtime specified. FPGA responds to a command sent by me on uart with fixed no of bytes. I can successfully read them for a long time, but after executing for a while, the code simply gets stuck up in the read function. For a blocking read, I would assume it to exit the routine after the vtime period specified even if it does not see any data on the uart buffer.But, it simply hangs the whole application.
Does anyone have an idea what must be causing this?
following are the properties i set for my uart port:
struct termios oldtio;
int fd;
fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
/* get old settings */
if (tcgetattr(fd, &oldtio) == -1) {
perror("tcgetattr");
}
/* no parity, CR=>NL */
oldtio.c_iflag |= IGNPAR | ICRNL;
/* XON/XOFF flow control off */
oldtio.c_iflag &= ~IXON;
oldtio.c_oflag = 0;
/* zero the character size bits */
oldtio.c_cflag &= ~CSIZE;
/* 8 data bits */
oldtio.c_cflag |= CS8 | CLOCAL | CREAD;
/* no hw flow control, no parity and 1 stop bit */
oldtio.c_cflag &= ~CRTSCTS & ~PARENB & ~CSTOPB;
if (cfsetispeed(&oldtio, B115200) == -1) {
perror("cfsetispeed");
exit(1);
}
if (cfsetospeed(&oldtio, B115200) == -1) {
perror("cfsetospeed");
exit(1);
}
/* raw mode */
//oldtio.c_lflag &= ~(ICANON | ISIG);
oldtio.c_lflag &= ~(ECHO | ICANON | ISIG);
oldtio.c_cc[VTIME] = vtime_value;
oldtio.c_cc[VMIN] = vmin_value;
if (tcflush(fd, TCIFLUSH) == -1) {
perror("tcflush");
exit(1);
}
if (tcsetattr(fd,TCSANOW,&oldtio) == -1) {
perror("tcsetattr");
exit(1);
}
baud_rate = 115200;
vmin = 0;
vtime=255;