Unix serialport program with qt crashes - c++

I am developing a simple serialport application with qt. I have configured the ttyUSB0 and i managed to open the port. But when datareceived from serialport my application closes. This is my code, Can anybody have an idea that what is wrong with this code ?
Thanks in advance,
#include "linkasunixserialport.h"
LinkasUnixSerialPort::LinkasUnixSerialPort()
{
fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1)
{
qDebug()<<"open_port: Unable to open /dev/ttyO1\n";
exit(1);
}
qDebug()<<"devttyUSB0 opened";
fcntl(fd, F_SETFL, FNDELAY);
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, O_ASYNC );
tcgetattr(fd,&termAttr);
cfsetispeed(&termAttr,B115200);
cfsetospeed(&termAttr,B115200);
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);
qDebug()<<"UART1 configured....\n";
this->start();
}
int LinkasUnixSerialPort::bytes_available()
{
int bytesQueued;
if (::ioctl(fd, FIONREAD, &bytesQueued) == -1) {
return -1;
}
qDebug()<<bytesQueued;
return bytesQueued;
}
void LinkasUnixSerialPort::run()
{
char receive_data[2];
forever
{
int bytes_read = bytes_available();
if(bytes_read > -1)
{
int retVal = ::read(fd, receive_data, 1);
if(retVal != -1)
{
emit dataReceived(QChar(receive_data[0]));
}
}
else
{
this->msleep(1);
}
}
}

It is maybe better to use Qt 5.1 SerialPort support for this use case:
See further information on QSerialPort documentation with examples on Qt Project Homepage.

Related

Why when I try set serial port, function return "Input/output error"?

I try set in c++ some serial port, but when I try function tcsetatrr, its return error -1. Port opens without problems.
char port_name[] = "/dev/ttyS1";
int port = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
if(port < 0){
std::cout << "Cant open port" << std::endl;
return;
}
struct termios settings;
tcgetattr(port, &settings);
cfgetispeed(&settings);
//std::cout << settings.c_cflag;
//Baudrate
cfsetispeed(&settings, B115200);
cfsetospeed(&settings, B115200);
//Data bits
settings.c_cflag &= ~CSIZE;
settings.c_cflag |= CS8;
//Parity
settings.c_cflag |= ~PARENB;
//Stop bit
settings.c_cflag &= ~CSTOPB;
//Flow control
settings.c_cflag |= ~CRTSCTS;
settings.c_iflag &= ~(IXON | IXOFF | IXANY);
int er = tcsetattr(port, TCSANOW, &settings);
if (er<0) {
fprintf(stderr, "Error openinig: %s\n", strerror(errno));
}
close(port);
output:
Input/output error
How can I fix that? I was running code as root. The problem is not in the configuration of the settings structure because after commenting it out, I get the same error
The most likely answer is that you don't really have a serial device /dev/ttyS1. Try /dev/ttyS0 instead. I have the same deal on my PC.
I see a few additional problems here.
You don't check if tcgetattr() succeeds, so don't be so sure you even get to tcsetattr().
Calling settings.c_cflag |= ~PARENB; or settings.c_cflag |= ~CRTSCTS; is most likely not what you want to do (will raise all flags except the selected one).

Set a callback function for I2C device to get data when IO event happens

I am trying to get data from a gyro using I2C interface and I can't figure out how can I set a event handler to check for IO instead of reading the FIFO buffer in an infinite loop.
I had done a similar thing using UART.
struct sigaction saio;
struct termios options;
uartFilestream = open(UART_DEV, O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
saio.sa_handler = HandleIO;
saio.sa_flags = 0;
saio.sa_restorer = NULL;
sigaction(SIGIO,&saio,NULL);
fcntl(uartFilestream, F_SETFL, FNDELAY);
fcntl(uartFilestream, F_SETOWN, getpid());
fcntl(uartFilestream, F_SETFL, O_ASYNC );
tcgetattr(uartFilestream, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uartFilestream, TCIFLUSH);
tcsetattr(uartFilestream, TCSANOW, &options);
HandleIO is called whenever IO happens on the the serial interface. Please tell me how can I do the same with I2C.
Here is what I did with I2C but it doesn't work.
fd = open("/dev/i2c-1", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
saio.sa_handler = test_func;
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, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(fd, TCIFLUSH);
tcsetattr(fd, TCSANOW, &options);

serial port: bytes from device all have their most significant bit = 0

guys tell me please what can be a problem - i send commands (sequence of bytes including bytes with most significant bit (MSB) = 1) to device through serial port, device successfully recognize the commands (so it seems all bytes are sent correct) and reply with almost correct answer. "Almost" here means than answer is correct except that all bytes have zero MSB (while some of them must have MSB = 1).
I use Ubuntu 16 on VirtualBox, Windows 7 as host OS, serial port as USB device on FTDI chip.
Here my serial port settings:
void com::open()
{
fd = ::open( "/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY );
if ( fd < 0 )
{
throw exceptionSystem( ERROR_UNK, "Error open COM port" );
}
int err;
err = fcntl(fd, F_SETFL, FNDELAY);
if ( err == -1 )
{
close();
throw exceptionSystem( ERROR_UNK, "Error fcntl" );
}
try{
set();
}catch(...){
close();
throw;
}
}
void com::set()
{
if ( fd == -1 )
return;
struct termios tty;
memset( &tty, 0, sizeof( tty ) );
if ( tcgetattr(fd, &tty) != 0 )
{
throw exceptionSystem( ERROR_UNK, "Error tcgetattr" );
}
int err;
err = cfsetospeed( &tty, B115200 );
if ( err != 0 )
{
throw exceptionSystem( ERROR_UNK, "Error fsetospeed" );
}
err = cfsetispeed( &tty, B115200 );
if ( err != 0 )
{
throw exceptionSystem( ERROR_UNK, "Error cfsetispeed" );
}
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
tty.c_cflag |= PARENB;
tty.c_cflag &= ~PARODD;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag |= CLOCAL;
tty.c_cflag |= CREAD;
tty.c_cflag &= ~CRTSCTS;
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tty.c_iflag |= (INPCK | ISTRIP);
tty.c_iflag &= ~IGNPAR;
tty.c_iflag &= ~PARMRK;
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_oflag &= ~OPOST;
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 0;
if ( tcsetattr( fd, TCSANOW, &tty ) != 0 )
{
throw exceptionSystem( ERROR_UNK, "Error tcsetattr" );
}
}
EDIT I've just seen the problem. You've got the line
tty.c_iflag |= (INPCK | ISTRIP);
ISTRIP means "strip off the eighth bit". You need
tty.c_iflag |= INPCK;
to simply enable input parity.
You've enabled EVEN parity
tty.c_cflag |= PARENB;
tty.c_cflag &= ~PARODD;
as well as 8-bit bytes
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8;
So that's one start bit, 8 data bits, 1 parity bit, and 1 stop bit. I'm not sure that the FTDI chip can handle all that!
If you don't want parity, use
tty.c_cflag &= ~PARENB;
And if I've maligned the FTDI chip, I apologise - in which case are you sure the other end is configured the same way? 8E1 is not very common compared to 7E1 or 8N1

Connect to serial device

I'm trying to write bytes to an serialport, where I connect a relay.
Everything works fine, if I use C++ for this: (comments are german, sorry)
#include <fcntl.h>
#include <termios.h>
RelayProtocol::RelayProtocol()
{
// Create structure
struct termios options;
// Open Connection
_handle = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY | O_NDELAY);
// clear all flags
fcntl(_handle, F_SETFL, 0);
// Get current options
tcgetattr(_handle, &options);
// Set Boundrate (19200)
cfsetispeed(&options, B19200); // Input
cfsetospeed(&options, B19200); // Output
// Set serial-flags
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag |= (CLOCAL | CREAD);
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
options.c_oflag &= ~OPOST;
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
// Flush settings
tcflush(_handle,TCIOFLUSH);
tcsetattr(_handle, TCSAFLUSH, &options);
fcntl(_handle, F_SETFL, FNDELAY);
fcntl(_handle, F_SETFL, 0);
// Initialize relay
SendCommand(1, 1, 0);
}
Now I tried the same using vala:
public static void main (string[] args) {
int handle = Posix.open ("/dev/ttyUSB0", Posix.O_RDWR | Posix.O_NOCTTY | O_NDELAY);
Posix.termios termios;
Posix.fcntl (handle, Posix.F_SETFL, 0);
Posix.tcgetattr (handle, out termios);
termios.c_ispeed = 19200;
termios.c_ospeed = 19200;
/* Configure serialport */
termios.c_cflag &= ~Posix.PARENB;
termios.c_cflag &= ~Posix.CSTOPB;
termios.c_cflag &= ~Posix.CSIZE;
termios.c_cflag |= Posix.CS8;
termios.c_cflag |= (Posix.CLOCAL | Posix.CREAD);
termios.c_lflag &= ~(Posix.ICANON | Posix.ECHO | Posix.ECHOE | Posix.ISIG);
termios.c_oflag &= ~Posix.OPOST;
termios.c_cc[Posix.VMIN] = 0;
termios.c_cc[Posix.VTIME] = 10;
Posix.tcflush (handle, Posix.TCIOFLUSH);
Posix.tcsetattr (handle, Posix.TCSAFLUSH, termios);
Posix.fcntl (handle, Posix.F_SETFL, FNDELAY);
Posix.fcntl (handle, Posix.F_SETFL, 0);
/* Everything works fine, if I open it using tail -f /dev/ttyUSB0*/
uint8[] data = new uint8[4];
data[0] = 1;
data[1] = 1;
data[2] = 0;
data[3] = data[0] ^ data[1] ^ data[2];
int l = (int)Posix.write (handle, data, data.length);
Thread.usleep (1000 * 1000);
data[0] = make_array (2, true)[0];
data[1] = make_array (2, true)[1];
data[2] = make_array (2, true)[2];
data[3] = data[0] ^ data[1] ^ data[2];
l = (int)Posix.write (handle, data, data.length);
}
I have created two variables on the top, which doesn't exist in the posix namespace. I simply print the value in C.
This code in vala doesn't work, the relay doesn't receive anything, although it uses the same flags and handles.
The strange thing is, that the vala code works fine, if I open a terminal and run "tail -f /dev/ttyUSB0".
It seems that the valacode isn't able to initialize the serial port, so relay will never receive them.
Anybody know a fix for this, or find my mistake in the vala code?
Normally the librarys should be the same in both programming languages.

struct termios setting for serial communication with arduino

on a unix based software, which must send a number between 0 and 179 to arduino and arduino will apply that number as an angle to a servo motor, but i do not know what parameters i have to change in the terminos struct to permit the serial communication.
this is the c++ code:
#include <iostream>
#include <unistd.h>
#include <fstream>
#include <termios.h>
using namespace std;
int main()
{
unsigned int angle;
ofstream arduino;
struct termios ttable;
//cout<<"test-1";
arduino.open("/dev/tty.usbmodem3a21");
//cout<<"test-2";
if(!arduino)
{
cout<<"\n\nERR: could not open port\n\n";
}
else
{
if(tcgetattr(arduino,&ttable)<0)
{
cout<<"\n\nERR: could not get terminal options\n\n";
}
else
{
//there goes the terminal options setting for the output;
ttable.c_cflag = -hupcl //to prevent the reset of arduino
cfsetospeed(&ttable,9600);
if(tcsetattr(arduino,TCSANOW,&ttable)<0)
{
cout<<"\n\nERR: could not set new terminal options\n\n";
}
else
{
do
{
cout<<"\n\ninsert a number between 0 and 179";
cin>>angle;
arduino<<angle;
}while(angle<=179);
arduino.close();
}
}
}
}
and this is arduino’s :
#include <Servo.h>
Servo servo;
const int pinServo = 2;
unsigned int angle;
void setup()
{
Serial.begin(9600);
servo.attach(pinServo);
servo.write(0);
}
void loop()
{
if(Serial.available()>0)
{
angle = Serial.read();
if(angle <= 179)
{
servo.write(angle);
}
}
}
So would you kindly tell me what do i have to change of "ttable" ?
In general, talking to an Arduino from C/C++ is easiest with the serial port in 'raw' mode. This is basically 8N1, byte-at-a-time, with the TTY doing the minimal amount of futzing about with the data. An easy way to set the various flags in a termios struct for this mode is to use cfmakeraw(3). Per the man pages this does the following:
struct termios config;
config.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
INLCR | IGNCR | ICRNL | IXON);
config.c_oflag &= ~OPOST;
config.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
config.c_cflag &= ~(CSIZE | PARENB);
config.c_cflag |= CS8;
For good measure, explicitly set receive enable and ignore modem control with config.c_cflag |= (CLOCAL | CREAD);. If the termios structure you are using is a copy of the existing one (obtained with tcgetattr(), for example), then also disable flow control altogether with config.c_iflag &= ~(IXOFF | IXANY);. So all-in-all, it will look like:
struct termios config;
cfmakeraw(&config);
config.c_cflag |= (CLOCAL | CREAD);
config.c_iflag &= ~(IXOFF | IXANY);
// set vtime, vmin, baud rate...
config.c_cc[VMIN] = 0; // you likely don't want to change this
config.c_cc[VTIME] = 0; // or this
cfsetispeed(&config, B9600);
cfsetospeed(&config, B9600);
// write port configuration to driver
tcsetattr(fd, TCSANOW, &config;
Using C++ file streams is a bit tricky as well. You very likely want non-blocking read/write and no controlling TTY, so it is usually easier to use open(2) with the O_NOCTTY and O_NDELAY flags set.
some thing like that for termios is best option
options.c_cflag &= ~CRTSCTS;
options.c_cflag |= (CLOCAL | CREAD);
options.c_iflag |= (IGNPAR | IGNCR);
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_oflag &= ~OPOST;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
options.c_iflag &= ~(ICRNL|IGNCR);
options.c_cflag &= ~CSTOPB;
options.c_iflag |= INPCK;
options.c_cc[VTIME] = 0.001; // 1s=10 0.1s=1 *
options.c_cc[VMIN] = 0;