Can't read from serial device - c++

I'm trying to write a library to read data from a serial device, a Mipex-02 gas sensor. Unfortunately, my code doesn't seem to open the serial connection properly, and I can't figure out why.
The full source code is available on github, specifically, here's the configuration of the serial connection:
MipexSensor::MipexSensor(string devpath) {
if (!check_dev_path(devpath))
throw "Invalid devpath";
this->path = devpath;
this->debugout_ = false;
this->sensor.SetBaudRate(SerialStreamBuf::BAUD_9600);
this->sensor.SetCharSize(SerialStreamBuf::CHAR_SIZE_8);
this->sensor.SetNumOfStopBits(1);
this->sensor.SetParity(SerialStreamBuf::PARITY_NONE);
this->sensor.SetFlowControl(SerialStreamBuf::FLOW_CONTROL_NONE);
this->last_concentration = this->last_um = this->last_ur = this->last_status = 0;
cout << "Connecting to "<< devpath << endl;
this->sensor.Open(devpath);
}
I think the meaning of the enums here are obvious enough. The values are from the instruction manual:
UART characteristics:
exchange rate – 9600 baud,
8-bit message,
1 stop bit,
without check for parity
So at first I was using interceptty to test it, and it worked perfectly fine. But when I tried to connect to the device directly, I couldn't read anything. the RX LED flashes on the devices so clearly the program manages to send something, but -unlike with interceptty- the TX LED never flash.
So I don't know if it's sending data incorrectly, if it's not sending all of it, and I can't even sniff the connection since it only happens when interceptty isn't in the middle. Interceptty's command-line is interceptty -s 'ispeed 9600 ospeed 9600 -parenb -cstopb -ixon cs8' -l /dev/ttyUSB0 (-s options are passed to stty) which is in theory the same options set in the code.
Thanks in advance.

Related

Qt Serial Communication not sending all data

I am writing a Qt application for serial communication with a Qorvo MDEK-1001. All built-in serial commands I've had to use work fine except for one: aurs n k, where n and k are integers corresponding to the desired rate of data transmission (e.g. "aurs 1 1\r"). Write function is:
void MainWindow::serialWrite(const QByteArray &command)
{
if(mdek->isOpen())
{
mdek->write(command);
qDebug() << "Command: " << command;
//mdek->flush();
}
}
If I send the command "aurs 1 1\r". It doesn't actually get sent to the device until I send another command for some reason. So if I subsequently send the "quit" command to the device, the returned data from the device is: "aurs 1quit", which registers as an unknown command. Any assistance getting this command to send properly is appreciated.
I've tried a bunch of stuff (setting bytes to write as second parameter in write(), using QDataStream, appending individual hex bytes onto QByteArray and writing that), but nothing has worked. This is the first time I've had to use Qt's serial communication software so I've probably missed something obvious.
On Linux Manjaro (same thing happens on Windows 8.1)
Connection settings: 8 data bits, Baud: 115200, No Flow Control, No Parity, One Stop Bit

Boost ASIO Serial Communication with Arduino: Error above 9600 bps

I'm developing a C++ interface to communicate with na Arduino UNO which is running some code.
To communicate with the Arduino, i'm using boost asio library. My application works well at a baud rate of 9600bps. Now, i wanted to communicate faster with the arduino, so, i tried to communicate at 115200bps, 57600bps, etc, without success.
At 115200bps, it seems that boost::write is sending two non-desirable bytes (both the same, with value ASCII 240 - this only happens for the first data transaction, so if i unplug and plug again the Arduino, this bytes will be sent during the first communication ). At this baud rate i can read the data that is being sent by Arduino (which is wrong for the first data communication, but is correct for the next ones).
At 57600bps, those 2 wrong bytes are not sent, but data is not read from arduino (it seems that write is not sending nothing).
The code to write to the serial is fairly simple, is just the boost::write and the code to read from the serial is just a loop and a boost::read of one byte (communications are synchronous just to test if everything was okay, which is not for higher baud rates than 9600bps).
The write function:
void sendMessage(char *c, unsigned int size) {
serial.write(c, size);
return;
}
The read function:
void readMessage(void) {
char c;
uint8_t count = 0;
for (;;)
{
boost::asio::read(serial, boost::asio::buffer(&c, 1));
cout << "Received char: " << static_cast<unsigned int>(c) << endl;
if (count == 3 ){
return;
}
count++;
}
return;
}
I know that the problem is not in the side of the arduino (that's why i posted the question here and not in the arduino stackexchange) because, using realterm and sending the exact same bytes that i send using boost, i get the proper reply from the Arduino for every baud rate (9600, 57600 and 115200bps).
If anyone can help, i would be appreciated, since at this moment i don't know which is the problem (and i'm a beginner to boost).
Best regards
Edit
At 74880 bps, I recieve four times the byte with value 252.

How to read from serial device in C++

I'm trying to figure out how I should read/write to my Arduino using serial communication in Linux C++. Currently I'm trying to read a response from my Arudino that I "trigger" with
echo "g" > /dev/ttyACM0"
I've tried looking at the response from my Arduino in my terminal, by using the following command:
tail -f /dev/ttyACM0
This is working as it should. Now I want to do the same thing in a C++ application. So I made this test
void testSerialComm()
{
std::string port = "/dev/ttyACM0";
int device = open(port.c_str(), O_RDWR | O_NOCTTY | O_SYNC);
std::string response;
char buffer[64];
do
{
int n = read(device, buffer, sizeof buffer);
if (n > 0) {
response += std::string(buffer);
std::cout << buffer;
}
} while (buffer[0] != 'X'); // 'X' means end of transmission
std::cout << "Response is: " << std::endl;
std::cout << response << std::endl;
close(device);
}
After a few "messages", the transmission gets a little messed up. My test application writes the response characters in random order and something's not right. I tried configuring the /dev/ttyACM0 device by this command:
stty -F /dev/ttyUSB0 cs8 115200 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts
No dice. Can someone help me understand how to communicate with my Arduino in C++?
The shown code opens /dev/ttyACM0, attempts to seek to the end of this "file", and based on the resulting file position allocates an old-fashioned, C-style memory buffer.
The problem with this approach is that you can only seek through regular, plain, garden-variety files. /dev/ttyACM0 is not a regular file. It's a device. Although some devices are seekable, this one isn't. Which, according to the comments, you've discovered independently.
Serial port devices are readable and writable. They are not seekable. There's no such thing as "seek"ing on a serial port. That makes no sense.
To read from the serial port you just read it, that's all. The operating system does maintain an internal buffer of some size, so if some characters were already received over the serial port, the initial read will return them all (provided that the read() buffer size is sufficiently large). If you pass a 1024 character buffer, for example, and five characters were already read from the serial port read() will return 5, to indicate that accordingly.
If no characters have been read, and you opened the serial port as a blocking device, read() will block at least until one character has been read from the serial port, and then return.
So, in order to read from the serial port all you have to do is read from it, until you've decided that you've read all there is to read from it. How do you decide that? That's up to you. You may decide that you want to read only until reading a newline character. Or you may decide that you want to read only until a fixed #n number of characters have been read. That's entirely up to you.
And, of course, if the hardware is suitably arranged, and you make the necessary arrangements with the serial port device to respect the serial port control pins, and, depending on your configuration, the DCD and/or DSR pins are signaled to indicate that the serial port device is no longer available, your read() will immediately return 0, to indicate a pseudo-end of file condition on the serial port device. That's also something that you will need to implement the necessary logic to handle.
P.S. neither C-style stdio, nor C++-style iostreams will work quite well with character devices, due to their own internal buffering algorithms. When working with serial ports, using open(2), read(2), write(2), and close(2) is better. But all of the above still applies.

Communication Arduino-C++ do not read Arduino

I have the following code:
QSerialPort arduPort("COM5");
arduPort.setBaudRate(QSerialPort::Baud9600);
arduPort.setDataBits(QSerialPort::Data8);
arduPort.setParity(QSerialPort::NoParity);
arduPort.setStopBits(QSerialPort::OneStop);
arduPort.setFlowControl(QSerialPort::NoFlowControl);
arduPort.open(QSerialPort::ReadWrite);
cout<<arduPort.isReadable()<<endl;
cout<<arduPort.isWritable()<<endl;
arduPort.write("a");
QByteArray s=arduPort.readAll();
cout<<QString(s).toStdString()<<endl;
And the next code in Arduino:
int inByte = 0;
void setup()
{
Serial.begin(9600);
while(!Serial){;}
int i=0;
}
void loop()
{
if(Serial.read()=='a')
Serial.write('b');
}
First I send an 'a' to the Arduino, and the ARduino must respond with 'b'. But when I read the port of the Arduino, I recieve '' only.
Anyone knows why I recieve '' instead of 'b'? Thanks for your time.
Update: See bottom of this answer for the answer. TL;DR: You have so set the baud rate (and presumably all the other settings) after you open the port.
I believe this is a bug in the Windows implementation of QSerialPort. I haven't been able to narrow down the cause yet but I have the following symptoms:
Load the Arduino (Uno in my case; Leonardo may behave very differently) with the ASCII demo. Unplug and replug the Arduino. Note that the TX light doesn't come on.
Connect to it with Putty or the Arduino serial port monitor. This resets the Arduino and then prints the ASCII table. The TX light is on continuously as expected.
Unplug/replug the Arduino and this time connect to it with a QSerialPort program. This time despite the port being opened ok the TX light never comes on and readyRead() is never triggered. Also note that the Arduino is not reset because by default QSerialPort does not change DTR. If you do QSerialPort::setDataTerminalReady(false); then pause for 10ms then set it true it will reset the Arduino as expected but it still doesn't transmit.
Note that if you have an Arduino program that transmits data continuously (ASCII example stops), if you open the port with putty so that it starts transmitting and then open it with QSerialPort without unplugging the cable it will work! However as soon as you unplug/plug the cable it stops working again.
This makes me suspect that putty is setting some serial port option that is required by the arduino and reset when you replug the cable. QSerialPort obviously doesn't change this value.
Here are the settings used by Putty as far as I can tell:
dcb.fBinary = TRUE;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fDsrSensitivity = FALSE;
dcb.fTXContinueOnXoff = FALSE;
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
dcb.fAbortOnError = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = FALSE;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.BaudRate = ...;
dcb.ByteSize = ...;
And by QSerialPort:
dcb.fBinary = TRUE;
dcb.fDtrControl = unchanged!
dcb.fDsrSensitivity = unchanged!
dcb.fTXContinueOnXoff = unchanged!
dcb.fOutX = FALSE;
dcb.fInX = FALSE;
dcb.fErrorChar = FALSE;
dcb.fNull = FALSE;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fAbortOnError = FALSE;
dcb.fOutxCtsFlow = FALSE;
dcb.fOutxDsrFlow = unchanged!
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
dcb.BaudRate = ...;
dcb.ByteSize = ...;
So I think it must be one of those unchanged values which makes the Arduino think that it isn't connected. From the DCB documentation I suspect fTxContinueOnXoff.
Ok I am going to write a little program to read these settings and see what changes.
Update 1
Ok I wrote my program and made the following discovery. The differences after running putty and just my Qt program were:
BaudRate: THIS WASN'T SET BY QT!!!!!!! It turns out you can only set the baud rate after you open the port.. Otherwise it is left at the previous value which is 0 when you first plug in the cable.
fDtrControl: Set to 1 by Putty, left at 0 by Qt.
fOutX and fInX: Both also set to 1 by Putty and left at 0 by Qt.
After moving all my set...() function calls after the open it worked perfectly. I didn't have to fiddle with DtrControl or Out/InX. (Although I have also set DTR high manually.)
Update 2
While setting all the parameters I thought it would be a good idea to set the error policy to 'skip'. DON'T DO THIS! LEAVE IT ON IGNORE! Otherwise it fucks everything up and adds weird delays to all your communications.
Setting the ports before open is not quite possible before Qt 5.2. The reason is that the original design was a bit too low-level for the class rather properly object oriented. I had been considered for a long-time whether to change it and in the end I actually decided to do so.
I have just submitted a change that is now under code review that will make your original concept work, too.
Here you can find the details:
Make it possible to set the port values before opening
The summary can be read here for the change:
Make it possible to set the port values before opening
This patch also changes the behavior of the open method. We do not use port
detection anymore for good. It has been a broken concept, and it is very
unlikely that anyone has ever relied on it. If anyone had done that, they would
be in trouble anyway, getting noisy warnings needlessly and all that.
The other option was also considered to keep this behavior optionally as the
default, but that would complicate the API too much without not much gain.
The default port settings are now also the sane 9600,8,N,1, and no flow control.
Also please note that the serial port is closed automatically in the open method
if any of the settings fails.
Please update your Qt version (at least to Qt 5.2) or you can backport the change yourself. Then, it is possible to write this code and actually it is even recommended:
QSerialPort arduPort("COM5");
arduPort.setBaudRate(QSerialPort::Baud9600);
arduPort.setDataBits(QSerialPort::Data8);
arduPort.setParity(QSerialPort::NoParity);
arduPort.setStopBits(QSerialPort::OneStop);
arduPort.setFlowControl(QSerialPort::NoFlowControl);
arduPort.open(QSerialPort::ReadWrite);
BaudRate: THIS WASN'T SET BY QT!!!!!!! It turns out you can only set
the baud rate after you open the port.. Otherwise it is left at the
previous value which is 0 when you first plug in the cable.
Yes, it is true. But it has been fixed and will be available in Qt 5.3
fDtrControl: Set to 1 by Putty, left at 0 by Qt.
No. Qt do not touch the DTR signal at opening. This signal will be cleared only when was set to DTR_CONTROL_HANDSHAKE. Because QtSerialPort do not support the DTR/DSR flow control. So, in any case you can control the DTR by means of QSerialPort::setDataTerminalReady(bool).
PS: I mean current Qt 5.3 release
fOutX and fInX: Both also set to 1 by Putty and left at 0 by Qt.
This flag's are used only when you use QSerialPort::Software flow control (Xon/Xoff). But you are use the QSerialPort::NoFlowControl (as I can see from your code snippet), so, all right. So, please check that you use Putty with the "None" flow control too.
While setting all the parameters I thought it would be a good idea to
set the error policy to 'skip'.
Please use only QSerialPort::Ignore policy (by default). Because other policies is deprecated (all policies) and will be removed in future.
UPD:
dcb.fRtsControl = RTS_CONTROL_ENABLE;
Ahh, seems that your Arduino expect that RTS signal should be enabled by default. In this case you should to use QSerialPort::setRequestToSend(bool). But it is possible only in QSerialPort::NoFlowControl mode.
I.e. RTS always will be in RTS_CONTROL_DISABLE or RTS_CONTROL_HANDSHAKE after opening of port (depends on your FlowControl settings, QSerialPort::setFlowControl() ).

Boost asio serial port initially sending bad data

I am attempting to use boost asio for serial communication. I am currently working in Windows, but will eventually be moving the code into Linux. When ever I restart my computer data sent from the program is not what it should be (for example I send a null followed by a carriage return and get "00111111 10000011" in binary) and it is not consistent (multiple nulls yield different binary).
However, as soon as I use any other program to send any data to the serial port and run the program again it works perfectly. I think I must be missing something in the initialization of the port, but my research has not turned anything up.
Here is how I am opening the port:
// An IOService to get the socket to work
boost::asio::io_service *io;
// An acceptor for getting connections
boost::shared_ptr<boost::asio::serial_port> port;
// Cnstructor Functions
void Defaults() {
io = new boost::asio::io_service();
// Set Default Commands
command.prefix = 170;
command.address = 3;
command.xDot[0] = 128;
command.xDot[1] = 128;
command.xDot[2] = 128;
command.throtle = 0;
command.button8 = 0;
command.button16 = 0;
command.checkSum = 131;
}
void Defaults(char * port, int baud) {
Defaults();
// Setup the serial port
port.reset(new boost::asio::serial_port(*io,port));
port->set_option( boost::asio::serial_port_base::baud_rate( baud ));
// This is for testing
printf("portTest: %i\n",(int)port->is_open());
port->write_some(boost::asio::buffer((void*)"\0", 1));
boost::asio::write(*port, boost::asio::buffer((void*)"\0", 1));
boost::asio::write(*port, boost::asio::buffer((void*)"\r", 1));
boost::asio::write(*port, boost::asio::buffer((void*)"\r", 1));
Sleep(2000);
}
Edit: In an attempt to remove unrelated code I accidentally deleted the the line where I set the baud rate, I added it back. Also, I am checking the output with a null-modem and Docklight. Aside from the baud rate I am using all of the default serial settings specified for a boost serial port (I have also tried explicitly setting them with no effect).
You haven't said how you're checking what's being sent, but it's probably a baud rate mismatch between the two ends.
It looks like you're missing this:
port->set_option( boost::asio::serial_port_base::baud_rate( baud ) );
Number of data bits, parity, and start and stop bits will also need configuring if they're different to the default values.
If you still can't sort it, stick an oscilloscope on the output and compare the waveform of the sender and receiver. You'll see something like this.
This is the top search result when looking for this problem, so I thought I'd give what I believe is the correct answer:
Boost Asio is bugged as of this writing and does not set default values for ports on the Windows platform. Instead, it grabs the current values on the port and then bakes them right back in. After a fresh reboot, the port hasn't been used yet, so its Windows-default values are likely not useful for communication (byte size typically defaults to 9, for example). After you use the port with a program or library that sets the values correctly, the values Asio picks up afterward are correct.
To solve this until Asio incorporates the fix, just set everything on the port explicitly, ie:
using boost::asio::serial_port;
using boost::asio::serial_port_base;
void setup_port(serial_port &serial, unsigned int baud)
{
serial.set_option(serial_port_base::baud_rate(baud));
serial.set_option(serial_port_base::character_size(8));
serial.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none));
serial.set_option(serial_port_base::parity(serial_port_base::parity::none));
serial.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one));
}