Connect to serial device - c++

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.

Related

Serial port read/open error in C, Linux

I am having trouble selecting correct settings for the serial port to be opened.
Information I have is the following:
Synchronization: Asynchronous method
Communication method: Full duplex transmission
Communication speed: 9600 bps (bits per second)
Transmission code: 8-bit data
Data configuration: Start bit 1, data 8-bit + parity 1, stop bit 1
Error control: Horizontal (CRC) and vertical (even number) parities
1 byte configuration
PC should not use a control signal (DTR, DSR, RTS and CTS) at the time of this connection.
What I have is something like:
bool configurePort(void) {
struct termios port_settings;
bzero(&port_settings, sizeof(port_settings));
tcgetattr(fd, &port_settings);
cfsetispeed(&port_settings, B9600);
cfsetospeed(&port_settings, B9600);
port_settings.c_cflag &= ~CSIZE;
port_settings.c_cflag |= CS8;
// parity bit
//port_settings.c_cflag &= ~PARENB;
//port_settings.c_cflag &= ~PARODD;
// hardware flow
port_settings.c_cflag &= ~CRTSCTS;
// stop bit
//port_settings.c_cflag &= ~CSTOPB;
port_settings.c_iflag = IGNBRK;
port_settings.c_iflag &= ~(IXON | IXOFF | IXANY);
port_settings.c_lflag = 0;
port_settings.c_oflag = 0;
port_settings.c_cc[VMIN] = 1;
port_settings.c_cc[VTIME] = 0;
port_settings.c_cc[VEOF] = 4;
tcsetattr(fd, TCSANOW, &port_settings);
return true;
}
Tried various modifications but nothing seems to work.
The device is connected over USB-serial (ttyUSB0) and I have permissions.
It opens device, sends (?) data but never gets anything back...
Can someone point me what should be done?
Try with this:
bool configurePort(void) {
struct termios port_settings;
bzero(&port_settings, sizeof(port_settings));
if(tcgetattr(fd, &port_settings) < 0) {
perror("tcgetattr");
return false;
}
cfmakeraw(&port_settings);
cfsetispeed(&port_settings, B9600);
cfsetospeed(&port_settings, B9600);
//input
port_settings.c_iflag &= ~(IXON | IXOFF | IXANY); //disable flow control
//local
port_settings.c_lflag = 0; // No local flags
//output
port_settings.c_oflag |= ONLRET;
port_settings.c_oflag |= ONOCR;
port_settings.c_oflag &= ~OPOST;
port_settings.c_cflag &= ~CRTSCTS; // Disable RTS/CTS
port_settings.c_cflag |= CREAD; // Enable receiver
port_settings.c_cflag &= ~CSTOPB;
tcflush(fd, TCIFLUSH);
if(tcsetattr(fd, TCSANOW, &port_settings) < 0) {
perror("tcsetattr");
return false;
}
int iflags = TIOCM_DTR;
ioctl(fd, TIOCMBIC, &iflags); // turn off DTR
return true;
} //configure port

Failed to configure device ttyUSB0 (Arduino) on Ubuntu, C++

I can open serial port, but I can't correctly configure this port for write (/dev/ttyUSB0).
Piece of code C++:
int
Platform::initConnection( const char* devicePath, int baudRate )
{
int fd = 0;
int ret = 0;
struct termios terminalOptions; // POSIX structure for configurating terminal devices
fd = open( devicePath, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
//fd = open( devicePath, O_RDWR | O_NOCTTY );
if (fd == -1)
{
this->setFail();
this->setErrorStr( "Failed to open: " + (std::string)devicePath + ". " + (std::string)strerror(errno) );
return -1;
}
memset( &terminalOptions, 0, sizeof( struct termios ) ); // Cleaning up the structure
cfmakeraw(&terminalOptions); //
cfsetspeed(&terminalOptions, baudRate);
/*terminalOptions.c_cflag = CLOCAL; // If CLOCAL is set, the line behaves as if DCD is always asserted.
// It is used when your device is local
terminalOptions.c_cflag |= CS8; // Character size mask
terminalOptions.c_cc[VMIN] = 24; // 1 second timeout
terminalOptions.c_cc[VTIME] = 0; // */
terminalOptions.c_cflag &= ~CRTSCTS;
terminalOptions.c_cflag |= (CLOCAL | CREAD);
terminalOptions.c_iflag |= (IGNPAR | IGNCR);
terminalOptions.c_iflag &= ~(IXON | IXOFF | IXANY);
terminalOptions.c_oflag &= ~OPOST;
terminalOptions.c_cflag &= ~CSIZE;
terminalOptions.c_cflag |= CS8;
terminalOptions.c_cflag &= ~PARENB;
terminalOptions.c_iflag &= ~INPCK;
terminalOptions.c_iflag &= ~(ICRNL|IGNCR);
terminalOptions.c_cflag &= ~CSTOPB;
terminalOptions.c_iflag |= INPCK;
terminalOptions.c_cc[VTIME] = 0.001; // 1s=10 0.1s=1 *
terminalOptions.c_cc[VMIN] = 0;
ret = ioctl( fd, TIOCSETA, &terminalOptions ); // Configuring the device
if (ret == -1)
{
this->setFail();
this->setErrorStr( "Failed to configure device: " + (std::string)devicePath + ". " + (std::string)strerror(errno) );
return -1;
}
return fd;
}
Error:
Failed to configure device: /dev/ttyUSB0. Inappropriate ioctl for device
Arduino UNO uses chipset CH340.
I have no idea about the resolve this problem. I'm hope for your help. Thanks!
Update:
Log from dmesg
[11840.346071] usb 2-1.2: new full-speed USB device number 5 using ehci-pci
[11840.439832] usb 2-1.2: New USB device found, idVendor=1a86, idProduct=7523
[11840.439844] usb 2-1.2: New USB device strings: Mfr=0, Product=2, SerialNumber=0
[11840.439850] usb 2-1.2: Product: USB2.0-Serial
[11840.440472] ch341 2-1.2:1.0: ch341-uart converter detected
[11840.442452] usb 2-1.2: ch341-uart converter now attached to ttyUSB0
Thanks to all. I found the solution on their own:
As autoreset on serial connection is activated by default on most boards, you need to disable this feature if you want to communicate directly with your board with the last command instead of a terminal emulator (arduino IDE, screen, picocom...). If you have a Leonardo board, you are not concerned by this, because it does not autoreset. If you have a Uno board, connect a 10 µF capacitor between the RESET and GND pins. If you have another board, connect a 120 ohms resistor between the RESET and 5V pins. See http://playground.arduino.cc/Main/DisablingAutoResetOnSerialConnection for more details.
Сhanged code
memset( &terminalOptions, 0, sizeof( struct termios ) );
tcgetattr(fd, &terminalOptions); //change
cfmakeraw(&terminalOptions);
cfsetspeed(&terminalOptions, baudRate);
terminalOptions.c_cflag = CLOCAL;
terminalOptions.c_cflag |= CS8;
terminalOptions.c_cc[VMIN] = 0;
terminalOptions.c_cc[VTIME] = 10;
terminalOptions.c_cflag = CLOCAL;
terminalOptions.c_cflag &= ~HUPCL; //change (disable hang-up-on-close to avoid reset)
ret = tcsetattr(fd, TCSANOW, &terminalOptions); //change
if (ret == -1)
{
this->setFail();
this->setErrorStr( "Failed to configure device: " + (std::string)devicePath + ". " + (std::string)strerror(errno) );
return -1;
}
return fd;
I also have arduino UNO and when I plug it via usb port it connects to /dev/ttyACM0 not ttyUSB0 you should also check ttyACM0 when you plug and unplug your arduino UNO.
It is also the case If you haven't installed arduino port driver

How to check serial port in linux work properly

I am going to use serial port in my C++ program linux ubuntu 10.4 ,this is my open port function :
int Recorder::OpenPort()
{
int intFd ;
struct termios options;
intFd=open("/dev/ttyS0", O_RDWR | O_NOCTTY| O_NDELAY);
if (intFd==-1){
perror("open_port: Unable to open /dev/ttyS0 - ");
}
fcntl(intFd, F_SETFL, FNDELAY); /*configuration the port*/
tcgetattr(intFd, &options);
//set baud rates at 115200
cfsetispeed(&options, B115200 );
cfsetospeed(&options, B115200);
//mask the character size to 8 bit data & no parity.CHARACTER SIZE SETTING
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
//setting hardware flow control
options.c_cflag |= CRTSCTS;
//Enable the receiver and set local mode .should always be enabled (enable receiver and dont change owner of port)
options.c_cflag |= (CLOCAL | CREAD);
//flush output buffer
options.c_lflag |= FLUSHO;
//choosing raw data disable echoing
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
//not setting software flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY);
options.c_oflag &= ~OPOST;
// //output changes :for mapping NL to CR-NL.
// options.c_oflag |= (OPOST | ONLCR );
//
// options.c_oflag |= (NL1 | CR1 | FFDLY);
tcsetattr(intFd, TCSANOW, &options);
return intFd;
}
and this is the sample where I check my serial number:
int serial=0;
ioctl(intFd, TIOCMGET, &serial);
//for ubuntu 7.04 and 10.04
if(serial==16390 || serial==16422 || serial==16454 || serial==16486){//no connection with serial port
i checked my program many times and serial is 16486 every times and it means I have no connection with serial port , I checked my serial cable and it was ok? so how can I Solve my problem?

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;

Unix serialport program with qt crashes

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.